feat(api): register CLI spec groups
This commit is contained in:
parent
edb1cf0c1e
commit
3b26a15048
4 changed files with 99 additions and 7 deletions
|
|
@ -39,7 +39,7 @@ func addSDKCommand(parent *cli.Command) {
|
|||
}
|
||||
|
||||
bridge := goapi.NewToolBridge("/tools")
|
||||
groups := []goapi.RouteGroup{bridge}
|
||||
groups := append(goapi.RegisteredSpecGroups(), bridge)
|
||||
|
||||
tmpFile, err := os.CreateTemp("", "openapi-*.json")
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -22,8 +22,7 @@ func addSpecCommand(parent *cli.Command) {
|
|||
)
|
||||
|
||||
cmd := cli.NewCommand("spec", "Generate OpenAPI specification", "", func(cmd *cli.Command, args []string) error {
|
||||
// Build spec from registered route groups.
|
||||
// Additional groups can be added here as the platform grows.
|
||||
// Build spec from all route groups registered for CLI generation.
|
||||
builder := &goapi.SpecBuilder{
|
||||
Title: title,
|
||||
Description: description,
|
||||
|
|
@ -31,11 +30,8 @@ func addSpecCommand(parent *cli.Command) {
|
|||
Servers: parseServers(servers),
|
||||
}
|
||||
|
||||
// Start with the default tool bridge — future versions will
|
||||
// auto-populate from the MCP tool registry once the bridge
|
||||
// integration lands in the local go-ai module.
|
||||
bridge := goapi.NewToolBridge("/tools")
|
||||
groups := []goapi.RouteGroup{bridge}
|
||||
groups := append(goapi.RegisteredSpecGroups(), bridge)
|
||||
|
||||
if output != "" {
|
||||
if err := goapi.ExportSpecToFile(output, format, builder, groups); err != nil {
|
||||
|
|
|
|||
|
|
@ -8,9 +8,32 @@ import (
|
|||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"forge.lthn.ai/core/cli/pkg/cli"
|
||||
|
||||
api "dappco.re/go/core/api"
|
||||
)
|
||||
|
||||
type specCmdStubGroup struct{}
|
||||
|
||||
func (specCmdStubGroup) Name() string { return "registered" }
|
||||
func (specCmdStubGroup) BasePath() string { return "/registered" }
|
||||
func (specCmdStubGroup) RegisterRoutes(rg *gin.RouterGroup) {}
|
||||
func (specCmdStubGroup) Describe() []api.RouteDescription {
|
||||
return []api.RouteDescription{
|
||||
{
|
||||
Method: "GET",
|
||||
Path: "/ping",
|
||||
Summary: "Ping registered group",
|
||||
Tags: []string{"registered"},
|
||||
Response: map[string]any{
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPISpecCmd_Good_CommandStructure(t *testing.T) {
|
||||
root := &cli.Command{Use: "root"}
|
||||
AddAPICommands(root)
|
||||
|
|
@ -131,6 +154,40 @@ func TestAPISpecCmd_Good_ServerFlagAddsServers(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestAPISpecCmd_Good_RegisteredSpecGroups(t *testing.T) {
|
||||
api.RegisterSpecGroups(specCmdStubGroup{})
|
||||
|
||||
root := &cli.Command{Use: "root"}
|
||||
AddAPICommands(root)
|
||||
|
||||
outputFile := t.TempDir() + "/spec.json"
|
||||
root.SetArgs([]string{"api", "spec", "--output", outputFile})
|
||||
root.SetErr(new(bytes.Buffer))
|
||||
|
||||
if err := root.Execute(); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("expected spec file to be written: %v", err)
|
||||
}
|
||||
|
||||
var spec map[string]any
|
||||
if err := json.Unmarshal(data, &spec); err != nil {
|
||||
t.Fatalf("expected valid JSON spec, got error: %v", err)
|
||||
}
|
||||
|
||||
paths, ok := spec["paths"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("expected paths object in generated spec, got %T", spec["paths"])
|
||||
}
|
||||
|
||||
if _, ok := paths["/registered/ping"]; !ok {
|
||||
t.Fatal("expected registered route group path in generated spec")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPISDKCmd_Bad_EmptyLanguages(t *testing.T) {
|
||||
root := &cli.Command{Use: "root"}
|
||||
AddAPICommands(root)
|
||||
|
|
|
|||
39
spec_registry.go
Normal file
39
spec_registry.go
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
package api
|
||||
|
||||
import "sync"
|
||||
|
||||
// specRegistry stores RouteGroups that should be included in CLI-generated
|
||||
// OpenAPI documents. Packages can register their groups during init and the
|
||||
// API CLI will pick them up when building specs or SDKs.
|
||||
var specRegistry struct {
|
||||
mu sync.RWMutex
|
||||
groups []RouteGroup
|
||||
}
|
||||
|
||||
// RegisterSpecGroups adds route groups to the package-level spec registry.
|
||||
// Nil groups are ignored. Registered groups are returned by RegisteredSpecGroups
|
||||
// in the order they were added.
|
||||
func RegisterSpecGroups(groups ...RouteGroup) {
|
||||
specRegistry.mu.Lock()
|
||||
defer specRegistry.mu.Unlock()
|
||||
|
||||
for _, group := range groups {
|
||||
if group == nil {
|
||||
continue
|
||||
}
|
||||
specRegistry.groups = append(specRegistry.groups, group)
|
||||
}
|
||||
}
|
||||
|
||||
// RegisteredSpecGroups returns a copy of the route groups registered for
|
||||
// CLI-generated OpenAPI documents.
|
||||
func RegisteredSpecGroups() []RouteGroup {
|
||||
specRegistry.mu.RLock()
|
||||
defer specRegistry.mu.RUnlock()
|
||||
|
||||
out := make([]RouteGroup, len(specRegistry.groups))
|
||||
copy(out, specRegistry.groups)
|
||||
return out
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue