Feat v0.8.5 extension composability (#72)
All checks were successful
CI/CD / detect-changes (push) Successful in 4s
CI/CD / test-runners (push) Has been skipped
CI/CD / e2e-smoke (push) Has been skipped
CI/CD / test-frontend (push) Successful in 6s
CI/CD / test-go-pg (push) Successful in 2m44s
CI/CD / test-sqlite (push) Successful in 2m54s
CI/CD / build-and-deploy (push) Successful in 52s
All checks were successful
CI/CD / detect-changes (push) Successful in 4s
CI/CD / test-runners (push) Has been skipped
CI/CD / e2e-smoke (push) Has been skipped
CI/CD / test-frontend (push) Successful in 6s
CI/CD / test-go-pg (push) Successful in 2m44s
CI/CD / test-sqlite (push) Successful in 2m54s
CI/CD / build-and-deploy (push) Successful in 52s
Co-authored-by: Jeffrey Smith <jasafpro@gmail.com> Co-committed-by: Jeffrey Smith <jasafpro@gmail.com>
This commit was merged in pull request #72.
This commit is contained in:
96
server/handlers/admin_slots.go
Normal file
96
server/handlers/admin_slots.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// slotInfo describes a declared slot and its contributors.
|
||||
type slotInfo struct {
|
||||
Host string `json:"host"`
|
||||
Description string `json:"description"`
|
||||
Context map[string]any `json:"context,omitempty"`
|
||||
Contributors []slotContributor `json:"contributors"`
|
||||
}
|
||||
|
||||
// slotContributor describes one package's contribution to a slot.
|
||||
type slotContributor struct {
|
||||
Package string `json:"package"`
|
||||
Label string `json:"label,omitempty"`
|
||||
Icon string `json:"icon,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
// ListSlots returns an aggregated map of all declared slots across installed
|
||||
// packages together with their contributors. Built on-the-fly from manifests.
|
||||
// GET /api/v1/admin/slots
|
||||
func (h *PackageHandler) ListSlots(c *gin.Context) {
|
||||
pkgs, err := h.stores.Packages.List(c.Request.Context())
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list packages"})
|
||||
return
|
||||
}
|
||||
|
||||
slots := map[string]*slotInfo{}
|
||||
|
||||
// Pass 1: collect declared slots from host surfaces
|
||||
for _, pkg := range pkgs {
|
||||
slotsRaw, ok := pkg.Manifest["slots"].(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
for name, v := range slotsRaw {
|
||||
fullName := pkg.ID + ":" + name
|
||||
entry, ok := v.(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
desc, _ := entry["description"].(string)
|
||||
var ctx map[string]any
|
||||
if c, ok := entry["context"].(map[string]any); ok {
|
||||
ctx = c
|
||||
}
|
||||
slots[fullName] = &slotInfo{
|
||||
Host: pkg.ID,
|
||||
Description: desc,
|
||||
Context: ctx,
|
||||
Contributors: []slotContributor{},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 2: collect contributions
|
||||
for _, pkg := range pkgs {
|
||||
if !pkg.Enabled {
|
||||
continue
|
||||
}
|
||||
contribs, ok := pkg.Manifest["contributes"].(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
for slotKey, v := range contribs {
|
||||
// Ensure the slot entry exists (contributor can arrive before host)
|
||||
if _, exists := slots[slotKey]; !exists {
|
||||
host := slotKey
|
||||
if idx := strings.Index(slotKey, ":"); idx >= 0 {
|
||||
host = slotKey[:idx]
|
||||
}
|
||||
slots[slotKey] = &slotInfo{
|
||||
Host: host,
|
||||
Contributors: []slotContributor{},
|
||||
}
|
||||
}
|
||||
contrib := slotContributor{Package: pkg.ID}
|
||||
if m, ok := v.(map[string]any); ok {
|
||||
contrib.Label, _ = m["label"].(string)
|
||||
contrib.Icon, _ = m["icon"].(string)
|
||||
contrib.Description, _ = m["description"].(string)
|
||||
}
|
||||
slots[slotKey].Contributors = append(slots[slotKey].Contributors, contrib)
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"data": slots})
|
||||
}
|
||||
Reference in New Issue
Block a user