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 67ba8b739b48ac4950acc8fec864f7a471dcbb62
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon May 18 15:12:56 2026 +0200

    TUI: add RestSpecDevConsole to expose OpenAPI spec content via dev-console
    
    New rest-spec dev console loads and returns the full content of OpenAPI
    specification files registered in RestRegistry (contract-first routes).
    Supports a FILTER option for pattern-matching on specificationUri.
    Both TEXT and JSON outputs; deduplicates specs shared across operations.
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
---
 .../org/apache/camel/dev-console/rest-spec.json    |  15 +++
 .../org/apache/camel/dev-console/rest-spec         |   2 +
 .../org/apache/camel/dev-consoles.properties       |   2 +-
 .../camel/impl/console/RestSpecDevConsole.java     | 138 +++++++++++++++++++++
 4 files changed, 156 insertions(+), 1 deletion(-)

diff --git 
a/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/rest-spec.json
 
b/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/rest-spec.json
new file mode 100644
index 000000000000..5a88465ffee8
--- /dev/null
+++ 
b/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/rest-spec.json
@@ -0,0 +1,15 @@
+{
+  "console": {
+    "kind": "console",
+    "group": "camel",
+    "name": "rest-spec",
+    "title": "Rest Spec",
+    "description": "OpenAPI specification content for contract-first REST 
services",
+    "deprecated": false,
+    "javaType": "org.apache.camel.impl.console.RestSpecDevConsole",
+    "groupId": "org.apache.camel",
+    "artifactId": "camel-console",
+    "version": "4.21.0-SNAPSHOT"
+  }
+}
+
diff --git 
a/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/rest-spec
 
b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/rest-spec
new file mode 100644
index 000000000000..242bf4b9225a
--- /dev/null
+++ 
b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/rest-spec
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.impl.console.RestSpecDevConsole
diff --git 
a/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-consoles.properties
 
b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-consoles.properties
index 7e237034a2d8..90006f59d203 100644
--- 
a/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-consoles.properties
+++ 
b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-consoles.properties
@@ -1,5 +1,5 @@
 # Generated by camel build tools - do NOT edit this file!
-dev-consoles=bean blocked browse circuit-breaker consumer context debug 
endpoint errors eval-language event gc health inflight internal-tasks 
java-security jvm log memory message-history processor producer properties 
receive reload rest route route-controller route-dump route-group 
route-structure send service simple-language source startup-recorder 
system-properties thread top trace transformers type-converters variables
+dev-consoles=bean blocked browse circuit-breaker consumer context debug 
endpoint errors eval-language event gc health inflight internal-tasks 
java-security jvm log memory message-history processor producer properties 
receive reload rest rest-spec route route-controller route-dump route-group 
route-structure send service simple-language source startup-recorder 
system-properties thread top trace transformers type-converters variables
 groupId=org.apache.camel
 artifactId=camel-console
 version=4.21.0-SNAPSHOT
diff --git 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/RestSpecDevConsole.java
 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/RestSpecDevConsole.java
new file mode 100644
index 000000000000..9e29fe2285b0
--- /dev/null
+++ 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/RestSpecDevConsole.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.impl.console;
+
+import java.io.InputStream;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.camel.spi.Resource;
+import org.apache.camel.spi.RestRegistry;
+import org.apache.camel.spi.annotations.DevConsole;
+import org.apache.camel.support.PatternHelper;
+import org.apache.camel.support.PluginHelper;
+import org.apache.camel.support.console.AbstractDevConsole;
+import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+
+@DevConsole(name = "rest-spec", displayName = "Rest Spec",
+            description = "OpenAPI specification content for contract-first 
REST services")
+public class RestSpecDevConsole extends AbstractDevConsole {
+
+    /**
+     * Filters specifications matching the given URI pattern (e.g. {@code 
*.yaml}, {@code petstore*})
+     */
+    public static final String FILTER = "filter";
+
+    public RestSpecDevConsole() {
+        super("camel", "rest-spec", "Rest Spec", "OpenAPI specification 
content for contract-first REST services");
+    }
+
+    @Override
+    protected String doCallText(Map<String, Object> options) {
+        String filter = (String) options.get(FILTER);
+        StringBuilder sb = new StringBuilder();
+
+        for (SpecEntry entry : collectSpecs(filter)) {
+            if (!sb.isEmpty()) {
+                sb.append("\n");
+            }
+            sb.append(String.format("Specification: %s", entry.uri()));
+            if (entry.routeId() != null) {
+                sb.append(String.format("%n    Route Id: %s", 
entry.routeId()));
+            }
+            if (entry.content() != null) {
+                sb.append("\n");
+                sb.append(entry.content());
+            } else {
+                sb.append(String.format("%n    (content not available)"));
+            }
+            sb.append("\n");
+        }
+
+        return sb.toString();
+    }
+
+    @Override
+    protected Map<String, Object> doCallJson(Map<String, Object> options) {
+        String filter = (String) options.get(FILTER);
+        JsonObject root = new JsonObject();
+        JsonArray list = new JsonArray();
+        root.put("specs", list);
+
+        for (SpecEntry entry : collectSpecs(filter)) {
+            JsonObject jo = new JsonObject();
+            jo.put("specificationUri", entry.uri());
+            if (entry.routeId() != null) {
+                jo.put("routeId", entry.routeId());
+            }
+            if (entry.content() != null) {
+                jo.put("content", entry.content());
+            }
+            list.add(jo);
+        }
+
+        return root;
+    }
+
+    private Set<SpecEntry> collectSpecs(String filter) {
+        Set<SpecEntry> result = new LinkedHashSet<>();
+        RestRegistry rr = PluginHelper.getRestRegistry(getCamelContext());
+        if (rr == null) {
+            return result;
+        }
+
+        // track which URIs we've already loaded to avoid duplicates (many 
operations share one spec)
+        Set<String> seen = new LinkedHashSet<>();
+
+        for (RestRegistry.RestService rs : rr.listAllRestServices()) {
+            String specUri = rs.getSpecificationUri();
+            if (specUri == null || seen.contains(specUri)) {
+                continue;
+            }
+            if (filter != null && !PatternHelper.matchPattern(specUri, 
filter)) {
+                continue;
+            }
+            seen.add(specUri);
+
+            String content = loadContent(specUri);
+            result.add(new SpecEntry(specUri, rs.getRouteId(), content));
+        }
+
+        return result;
+    }
+
+    private String loadContent(String specUri) {
+        try {
+            Resource resource = 
PluginHelper.getResourceLoader(getCamelContext()).resolveResource(specUri);
+            if (resource != null && resource.exists()) {
+                InputStream is = resource.getInputStream();
+                String data = IOHelper.loadText(is);
+                IOHelper.close(is);
+                return data;
+            }
+        } catch (Exception e) {
+            // ignore — content unavailable
+        }
+        return null;
+    }
+
+    private record SpecEntry(String uri, String routeId, String content) {
+    }
+}

Reply via email to