V0.38.4 full composition (#237)

Co-authored-by: gobha <jasafpro@gmail.com>
Co-committed-by: gobha <jasafpro@gmail.com>
This commit is contained in:
2026-03-25 17:29:08 +00:00
committed by xcaliber
parent fe5894b6e2
commit 495bcc94f4
17 changed files with 943 additions and 39 deletions

View File

@@ -0,0 +1,103 @@
package handlers
// v0.38.4: Connection type discovery endpoint.
// Returns merged connection types from all active packages.
// Library declarations take precedence over non-library declarations.
import (
"encoding/json"
"net/http"
"github.com/gin-gonic/gin"
"chat-switchboard/store"
)
// ConnectionTypeEntry is a single connection type in the API response.
type ConnectionTypeEntry struct {
Type string `json:"type"`
Label string `json:"label"`
PackageID string `json:"package_id"`
PackageTitle string `json:"package_title"`
Fields map[string]map[string]string `json:"fields"`
Scopes []string `json:"scopes,omitempty"`
}
// ConnectionTypeHandler serves GET /api/v1/connection-types.
type ConnectionTypeHandler struct {
stores store.Stores
}
func NewConnectionTypeHandler(s store.Stores) *ConnectionTypeHandler {
return &ConnectionTypeHandler{stores: s}
}
// ListConnectionTypes returns all connection types declared by active packages.
// Library packages take precedence when multiple packages declare the same type.
func (h *ConnectionTypeHandler) ListConnectionTypes(c *gin.Context) {
ctx := c.Request.Context()
pkgs, err := h.stores.Packages.List(ctx)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list packages"})
return
}
type entry struct {
ct ConnectionTypeEntry
isLibrary bool
}
seen := map[string]*entry{}
for _, pkg := range pkgs {
if pkg.Status != "active" || !pkg.Enabled {
continue
}
connsRaw, ok := pkg.Manifest["connections"]
if !ok {
continue
}
connsJSON, _ := json.Marshal(connsRaw)
var conns []struct {
Type string `json:"type"`
Label string `json:"label"`
Fields map[string]map[string]string `json:"fields"`
Scopes []string `json:"scopes"`
}
if err := json.Unmarshal(connsJSON, &conns); err != nil {
continue
}
isLib := pkg.Type == "library"
for _, cd := range conns {
if cd.Type == "" {
continue
}
existing, exists := seen[cd.Type]
// Library declarations take precedence
if exists && !isLib && existing.isLibrary {
continue
}
label := cd.Label
if label == "" {
label = cd.Type
}
seen[cd.Type] = &entry{
ct: ConnectionTypeEntry{
Type: cd.Type,
Label: label,
PackageID: pkg.ID,
PackageTitle: pkg.Title,
Fields: cd.Fields,
Scopes: cd.Scopes,
},
isLibrary: isLib,
}
}
}
result := make([]ConnectionTypeEntry, 0, len(seen))
for _, e := range seen {
result = append(result, e.ct)
}
c.JSON(http.StatusOK, gin.H{"data": result})
}