This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch feat/camel-tui
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 6c9b2fba3bf80bc9b1a303a079865753c5f481b9
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon May 18 14:55:37 2026 +0200

    TUI: expose specificationUri in RestRegistry and HTTP detail panel
    
    Thread the OpenAPI spec file path from RestOpenApiEndpoint through the
    registry to the TUI so the HTTP detail panel shows which spec file backs
    each contract-first route.
    
    - RestRegistry.RestService: add getSpecificationUri() (@Nullable, 4.21)
    - RestRegistry.addRestService(): add specificationUri parameter
    - DefaultRestRegistry: store and expose specificationUri in RestServiceEntry
    - RestOpenApiProcessor: passes endpoint.getSpecificationUri() when 
registering
    - RestEndpoint (REST DSL): passes null (code-first has no spec URI)
    - RestDevConsole: emits specificationUri in JSON and text output when 
present
    - CamelMonitor HTTP detail panel: shows Spec: <uri> when specificationUri 
is set
    
    Viewing the spec content via a key will be added separately (requires
    Camel ResourceResolver to handle classpath: and file: URIs).
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
---
 .../rest/openapi/RestOpenApiProcessor.java         |  3 +-
 .../camel/component/rest/DefaultRestRegistry.java  | 16 ++++++---
 .../apache/camel/component/rest/RestEndpoint.java  |  3 +-
 .../component/rest/DefaultRestRegistryTest.java    | 22 ++++++------
 .../component/rest/RestRegistryStatefulTest.java   |  6 ++--
 .../java/org/apache/camel/spi/RestRegistry.java    | 41 ++++++++++++++--------
 .../apache/camel/impl/console/RestDevConsole.java  |  6 ++++
 .../dsl/jbang/core/commands/tui/CamelMonitor.java  |  5 +++
 8 files changed, 67 insertions(+), 35 deletions(-)

diff --git 
a/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiProcessor.java
 
b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiProcessor.java
index c2680dda5eb6..a40a91f034a7 100644
--- 
a/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiProcessor.java
+++ 
b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiProcessor.java
@@ -174,7 +174,8 @@ public class RestOpenApiProcessor extends 
AsyncProcessorSupport implements Camel
                 }
                 RestRegistry restRegistry = 
PluginHelper.getRestRegistry(camelContext);
                 restRegistry.addRestService(consumer, true, url, path, 
basePath, null, v, bc.getConsumes(),
-                        bc.getProduces(), bc.getType(), bc.getOutType(), 
routeId, operationId, desc);
+                        bc.getProduces(), bc.getType(), bc.getOutType(), 
routeId, operationId,
+                        endpoint.getSpecificationUri(), desc);
 
                 try {
                     RestBindingAdvice binding = 
RestBindingAdviceFactory.build(camelContext, bc);
diff --git 
a/components/camel-rest/src/main/java/org/apache/camel/component/rest/DefaultRestRegistry.java
 
b/components/camel-rest/src/main/java/org/apache/camel/component/rest/DefaultRestRegistry.java
index 550b53cdcf6a..a29283973f64 100644
--- 
a/components/camel-rest/src/main/java/org/apache/camel/component/rest/DefaultRestRegistry.java
+++ 
b/components/camel-rest/src/main/java/org/apache/camel/component/rest/DefaultRestRegistry.java
@@ -51,10 +51,10 @@ public class DefaultRestRegistry extends ServiceSupport 
implements RestRegistry,
             Consumer consumer, boolean contractFirst, String url, String 
baseUrl, String basePath, String uriTemplate,
             String method,
             String consumes, String produces, String inType, String outType, 
String routeId, String operationId,
-            String description) {
+            String specificationUri, String description) {
         RestServiceEntry entry = new RestServiceEntry(
                 consumer, false, contractFirst, url, baseUrl, basePath, 
uriTemplate, method, consumes, produces, inType,
-                outType, routeId, operationId, description);
+                outType, routeId, operationId, specificationUri, description);
         List<RestService> list = registry.computeIfAbsent(consumer, c -> new 
ArrayList<>());
         list.add(entry);
     }
@@ -65,7 +65,7 @@ public class DefaultRestRegistry extends ServiceSupport 
implements RestRegistry,
             String produces, String description) {
         RestServiceEntry entry = new RestServiceEntry(
                 consumer, true, contractFirst, url, baseUrl, basePath, null, 
method, null, produces, null, null, null,
-                null, description);
+                null, null, description);
         List<RestService> list = specs.computeIfAbsent(consumer, c -> new 
ArrayList<>());
         list.add(entry);
     }
@@ -205,12 +205,14 @@ public class DefaultRestRegistry extends ServiceSupport 
implements RestRegistry,
         private final String outType;
         private final String routeId;
         private final String operationId;
+        private final String specificationUri;
         private final String description;
 
         private RestServiceEntry(Consumer consumer, boolean specification, 
boolean contractFirst, String url, String baseUrl,
                                  String basePath,
                                  String uriTemplate, String method, String 
consumes, String produces,
-                                 String inType, String outType, String 
routeId, String operationId, String description) {
+                                 String inType, String outType, String 
routeId, String operationId,
+                                 String specificationUri, String description) {
             this.consumer = consumer;
             this.specification = specification;
             this.contractFirst = contractFirst;
@@ -225,6 +227,7 @@ public class DefaultRestRegistry extends ServiceSupport 
implements RestRegistry,
             this.outType = outType;
             this.routeId = routeId;
             this.operationId = operationId;
+            this.specificationUri = specificationUri;
             this.description = description;
         }
 
@@ -312,6 +315,11 @@ public class DefaultRestRegistry extends ServiceSupport 
implements RestRegistry,
             return operationId;
         }
 
+        @Override
+        public String getSpecificationUri() {
+            return specificationUri;
+        }
+
         @Override
         public String getDescription() {
             return description;
diff --git 
a/components/camel-rest/src/main/java/org/apache/camel/component/rest/RestEndpoint.java
 
b/components/camel-rest/src/main/java/org/apache/camel/component/rest/RestEndpoint.java
index 710f31b96d45..926695b042fe 100644
--- 
a/components/camel-rest/src/main/java/org/apache/camel/component/rest/RestEndpoint.java
+++ 
b/components/camel-rest/src/main/java/org/apache/camel/component/rest/RestEndpoint.java
@@ -489,7 +489,8 @@ public class RestEndpoint extends DefaultEndpoint {
         // add to rest registry, so we can keep track of them
         RestRegistry registry = 
PluginHelper.getRestRegistry(getCamelContext());
         registry.addRestService(consumer, false, url, baseUrl, getPath(), 
getUriTemplate(),
-                getMethod(), getConsumes(), getProduces(), getInType(), 
getOutType(), getRouteId(), null, getDescription());
+                getMethod(), getConsumes(), getProduces(), getInType(), 
getOutType(), getRouteId(), null, null,
+                getDescription());
         return consumer;
     }
 
diff --git 
a/components/camel-rest/src/test/java/org/apache/camel/component/rest/DefaultRestRegistryTest.java
 
b/components/camel-rest/src/test/java/org/apache/camel/component/rest/DefaultRestRegistryTest.java
index 260e9983b46d..0d5c818106f2 100644
--- 
a/components/camel-rest/src/test/java/org/apache/camel/component/rest/DefaultRestRegistryTest.java
+++ 
b/components/camel-rest/src/test/java/org/apache/camel/component/rest/DefaultRestRegistryTest.java
@@ -62,7 +62,7 @@ class DefaultRestRegistryTest {
         registry.addRestService(consumer1, false, 
"http://localhost:8080/api/users";,
                 "http://localhost:8080";, "/api", "/users", "GET",
                 "application/json", "application/json", "User", "User",
-                "route1", null, "Get all users");
+                "route1", null, null, "Get all users");
 
         assertThat(registry.size()).isEqualTo(1);
     }
@@ -72,12 +72,12 @@ class DefaultRestRegistryTest {
         registry.addRestService(consumer1, false, 
"http://localhost:8080/api/users";,
                 "http://localhost:8080";, "/api", "/users", "GET",
                 "application/json", "application/json", null, null,
-                "route1", null, "Get all users");
+                "route1", null, null, "Get all users");
 
         registry.addRestService(consumer2, false, 
"http://localhost:8080/api/orders";,
                 "http://localhost:8080";, "/api", "/orders", "POST",
                 "application/json", "application/json", "Order", "Order",
-                "route2", null, "Create order");
+                "route2", null, null, "Create order");
 
         assertThat(registry.size()).isEqualTo(2);
     }
@@ -86,11 +86,11 @@ class DefaultRestRegistryTest {
     void testAddMultipleServicesToSameConsumer() {
         registry.addRestService(consumer1, false, 
"http://localhost:8080/api/users";,
                 "http://localhost:8080";, "/api", "/users", "GET",
-                null, null, null, null, "route1", null, "Get users");
+                null, null, null, null, "route1", null, null, "Get users");
 
         registry.addRestService(consumer1, false, 
"http://localhost:8080/api/users/{id}";,
                 "http://localhost:8080";, "/api", "/users/{id}", "GET",
-                null, null, null, null, "route2", null, "Get user by id");
+                null, null, null, null, "route2", null, null, "Get user by 
id");
 
         assertThat(registry.size()).isEqualTo(2);
     }
@@ -99,7 +99,7 @@ class DefaultRestRegistryTest {
     void testRemoveRestService() {
         registry.addRestService(consumer1, false, 
"http://localhost:8080/api/users";,
                 "http://localhost:8080";, "/api", "/users", "GET",
-                null, null, null, null, "route1", null, "Get users");
+                null, null, null, null, "route1", null, null, "Get users");
 
         assertThat(registry.size()).isEqualTo(1);
 
@@ -112,7 +112,7 @@ class DefaultRestRegistryTest {
     void testRemoveNonExistentService() {
         registry.addRestService(consumer1, false, 
"http://localhost:8080/api/users";,
                 "http://localhost:8080";, "/api", "/users", "GET",
-                null, null, null, null, "route1", null, "Get users");
+                null, null, null, null, "route1", null, null, "Get users");
 
         registry.removeRestService(consumer2);
 
@@ -124,7 +124,7 @@ class DefaultRestRegistryTest {
         registry.addRestService(consumer1, false, 
"http://localhost:8080/api/users";,
                 "http://localhost:8080";, "/api", "/users", "GET",
                 "application/json", "application/json", "User", "UserResponse",
-                "route1", null, "Get all users");
+                "route1", null, null, "Get all users");
 
         List<RestRegistry.RestService> services = 
registry.listAllRestServices();
 
@@ -149,7 +149,7 @@ class DefaultRestRegistryTest {
     void testContractFirstService() {
         registry.addRestService(consumer1, true, 
"http://localhost:8080/api/users";,
                 "http://localhost:8080";, "/api", "/users", "GET",
-                null, null, null, null, "route1", null, "Get users");
+                null, null, null, null, "route1", null, null, "Get users");
 
         List<RestRegistry.RestService> services = 
registry.listAllRestServices();
         assertThat(services.get(0).isContractFirst()).isTrue();
@@ -170,7 +170,7 @@ class DefaultRestRegistryTest {
     void testServiceState() {
         registry.addRestService(consumer1, false, 
"http://localhost:8080/api/users";,
                 "http://localhost:8080";, "/api", "/users", "GET",
-                null, null, null, null, "route1", null, "Get users");
+                null, null, null, null, "route1", null, null, "Get users");
 
         List<RestRegistry.RestService> services = 
registry.listAllRestServices();
         // Non-stateful consumer returns Stopped state
@@ -187,7 +187,7 @@ class DefaultRestRegistryTest {
     void testStopClearsRegistry() throws Exception {
         registry.addRestService(consumer1, false, 
"http://localhost:8080/api/users";,
                 "http://localhost:8080";, "/api", "/users", "GET",
-                null, null, null, null, "route1", null, "Get users");
+                null, null, null, null, "route1", null, null, "Get users");
 
         assertThat(registry.size()).isEqualTo(1);
 
diff --git 
a/components/camel-rest/src/test/java/org/apache/camel/component/rest/RestRegistryStatefulTest.java
 
b/components/camel-rest/src/test/java/org/apache/camel/component/rest/RestRegistryStatefulTest.java
index 7cf4922dc710..334afe6bfac8 100644
--- 
a/components/camel-rest/src/test/java/org/apache/camel/component/rest/RestRegistryStatefulTest.java
+++ 
b/components/camel-rest/src/test/java/org/apache/camel/component/rest/RestRegistryStatefulTest.java
@@ -73,7 +73,7 @@ class RestRegistryStatefulTest {
         registry.addRestService(consumer, false, 
"http://localhost:8080/api/users";,
                 "http://localhost:8080";, "/api", "/users", "GET",
                 "application/json", "application/json", null, null,
-                "route1", null, "Get users");
+                "route1", null, null, "Get users");
 
         List<RestRegistry.RestService> services = 
registry.listAllRestServices();
         assertThat(services).hasSize(1);
@@ -101,7 +101,7 @@ class RestRegistryStatefulTest {
 
         registry.addRestService(consumer, false, 
"http://localhost:8080/api/orders";,
                 "http://localhost:8080";, "/api", "/orders", "POST",
-                null, null, null, null, "route2", null, "Create order");
+                null, null, null, null, "route2", null, null, "Create order");
 
         List<RestRegistry.RestService> services = 
registry.listAllRestServices();
         assertThat(services.get(0).getState()).isEqualTo("Started");
@@ -117,7 +117,7 @@ class RestRegistryStatefulTest {
 
         registry.addRestService(consumer, false, 
"http://localhost:8080/api/items";,
                 "http://localhost:8080";, "/api", "/items", "DELETE",
-                null, null, null, null, "route3", null, "Delete item");
+                null, null, null, null, "route3", null, null, "Delete item");
 
         List<RestRegistry.RestService> services = 
registry.listAllRestServices();
         assertThat(services.get(0).getState()).isEqualTo("Suspended");
diff --git 
a/core/camel-api/src/main/java/org/apache/camel/spi/RestRegistry.java 
b/core/camel-api/src/main/java/org/apache/camel/spi/RestRegistry.java
index 852d3610e615..f5468065001d 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/RestRegistry.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/RestRegistry.java
@@ -129,31 +129,42 @@ public interface RestRegistry extends StaticService {
         @Nullable
         String getDescription();
 
+        /**
+         * Gets the specification URI of the OpenAPI contract (contract-first 
routes only), e.g. {@code petstore.yaml}
+         * or {@code classpath:openapi.json}.
+         *
+         * @since 4.21
+         */
+        @Nullable
+        String getSpecificationUri();
+
     }
 
     /**
      * Adds a new REST service to the registry.
      *
-     * @param consumer      the consumer
-     * @param contractFirst is the rest service based on code-first or 
contract-first
-     * @param url           the absolute url of the REST service
-     * @param baseUrl       the base url of the REST service
-     * @param basePath      the base path
-     * @param uriTemplate   the uri template
-     * @param method        the HTTP method
-     * @param consumes      optional details about what media-types the REST 
service accepts
-     * @param produces      optional details about what media-types the REST 
service returns
-     * @param inType        optional detail input binding to a FQN class name
-     * @param outType       optional detail output binding to a FQN class name
-     * @param routeId       the id of the route this rest service will be using
-     * @param operationId   optional operationId from the OpenAPI contract
-     * @param description   optional description about the service
+     * @param consumer         the consumer
+     * @param contractFirst    is the rest service based on code-first or 
contract-first
+     * @param url              the absolute url of the REST service
+     * @param baseUrl          the base url of the REST service
+     * @param basePath         the base path
+     * @param uriTemplate      the uri template
+     * @param method           the HTTP method
+     * @param consumes         optional details about what media-types the 
REST service accepts
+     * @param produces         optional details about what media-types the 
REST service returns
+     * @param inType           optional detail input binding to a FQN class 
name
+     * @param outType          optional detail output binding to a FQN class 
name
+     * @param routeId          the id of the route this rest service will be 
using
+     * @param operationId      optional operationId from the OpenAPI contract
+     * @param specificationUri optional URI of the OpenAPI spec file 
(contract-first only)
+     * @param description      optional description about the service
      */
     void addRestService(
             Consumer consumer, boolean contractFirst, String url, String 
baseUrl, String basePath,
             @Nullable String uriTemplate, String method,
             @Nullable String consumes, @Nullable String produces, @Nullable 
String inType, @Nullable String outType,
-            String routeId, @Nullable String operationId, @Nullable String 
description);
+            String routeId, @Nullable String operationId, @Nullable String 
specificationUri,
+            @Nullable String description);
 
     /**
      * Removes the REST service from the registry
diff --git 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/RestDevConsole.java
 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/RestDevConsole.java
index 73a80ea85919..dba1d20bc517 100644
--- 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/RestDevConsole.java
+++ 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/RestDevConsole.java
@@ -55,6 +55,9 @@ public class RestDevConsole extends AbstractDevConsole {
                 if (rs.getOperationId() != null) {
                     sb.append(String.format("%n    Operation Id: %s", 
rs.getOperationId()));
                 }
+                if (rs.getSpecificationUri() != null) {
+                    sb.append(String.format("%n    Specification: %s", 
rs.getSpecificationUri()));
+                }
                 if (rs.getConsumes() != null) {
                     sb.append(String.format("%n    Consumes: %s", 
rs.getConsumes()));
                 }
@@ -99,6 +102,9 @@ public class RestDevConsole extends AbstractDevConsole {
                 if (rs.getOperationId() != null) {
                     jo.put("operationId", rs.getOperationId());
                 }
+                if (rs.getSpecificationUri() != null) {
+                    jo.put("specificationUri", rs.getSpecificationUri());
+                }
                 if (rs.getConsumes() != null) {
                     jo.put("consumes", rs.getConsumes());
                 }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java
index f74897c9b834..ad69260b49d0 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java
@@ -3693,6 +3693,9 @@ public class CamelMonitor extends CamelCommand {
         if (ep.operationId != null) {
             addDetailLine(lines, "Operation", ep.operationId);
         }
+        if (ep.specificationUri != null) {
+            addDetailLine(lines, "Spec", ep.specificationUri);
+        }
         if (ep.state != null) {
             addDetailLine(lines, "State", ep.state);
         }
@@ -5431,6 +5434,7 @@ public class CamelMonitor extends CamelCommand {
                     ep.specification = 
Boolean.TRUE.equals(rj.get("specification"));
                     ep.routeId = rj.getString("routeId");
                     ep.operationId = rj.getString("operationId");
+                    ep.specificationUri = rj.getString("specificationUri");
                     ep.state = rj.getString("state");
                     ep.inType = rj.getString("inType");
                     ep.outType = rj.getString("outType");
@@ -5772,6 +5776,7 @@ public class CamelMonitor extends CamelCommand {
         boolean specification; // true = OpenAPI/Swagger spec endpoint
         String routeId;
         String operationId;
+        String specificationUri;
         String description;
         String inType;
         String outType;

Reply via email to