175 lines
5.6 KiB
Go
175 lines
5.6 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
|
|
"galaxy/backend/internal/runtime"
|
|
"galaxy/backend/internal/server/handlers"
|
|
"galaxy/backend/internal/server/httperr"
|
|
"galaxy/backend/internal/telemetry"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// AdminEngineVersionsHandlers groups the engine-version-registry
|
|
// handlers under `/api/v1/admin/engine-versions/*`. The implementation swaps
|
|
// the placeholder bodies for real `*runtime.EngineVersionService`
|
|
// calls; tests that omit the service fall back to the Stage-3 501
|
|
// envelope.
|
|
type AdminEngineVersionsHandlers struct {
|
|
svc *runtime.EngineVersionService
|
|
logger *zap.Logger
|
|
}
|
|
|
|
// NewAdminEngineVersionsHandlers constructs the handler set. svc may
|
|
// be nil — in that case every handler returns 501.
|
|
func NewAdminEngineVersionsHandlers(svc *runtime.EngineVersionService, logger *zap.Logger) *AdminEngineVersionsHandlers {
|
|
if logger == nil {
|
|
logger = zap.NewNop()
|
|
}
|
|
return &AdminEngineVersionsHandlers{svc: svc, logger: logger.Named("http.admin.engine-versions")}
|
|
}
|
|
|
|
// List handles GET /api/v1/admin/engine-versions.
|
|
func (h *AdminEngineVersionsHandlers) List() gin.HandlerFunc {
|
|
if h == nil || h.svc == nil {
|
|
return handlers.NotImplemented("adminEngineVersionsList")
|
|
}
|
|
return func(c *gin.Context) {
|
|
ctx := c.Request.Context()
|
|
items, err := h.svc.List(ctx)
|
|
if err != nil {
|
|
respondEngineVersionError(c, h.logger, "admin engine-versions list", ctx, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, engineVersionListToWire(items))
|
|
}
|
|
}
|
|
|
|
// Create handles POST /api/v1/admin/engine-versions.
|
|
func (h *AdminEngineVersionsHandlers) Create() gin.HandlerFunc {
|
|
if h == nil || h.svc == nil {
|
|
return handlers.NotImplemented("adminEngineVersionsCreate")
|
|
}
|
|
return func(c *gin.Context) {
|
|
var req engineVersionCreateRequestWire
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
httperr.Abort(c, http.StatusBadRequest, httperr.CodeInvalidRequest, "request body must be valid JSON")
|
|
return
|
|
}
|
|
ctx := c.Request.Context()
|
|
input := runtime.RegisterInput{Version: req.Version, ImageRef: req.ImageRef}
|
|
if req.Enabled != nil {
|
|
input.Enabled = req.Enabled
|
|
}
|
|
v, err := h.svc.Register(ctx, input)
|
|
if err != nil {
|
|
respondEngineVersionError(c, h.logger, "admin engine-versions create", ctx, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusCreated, engineVersionToWire(v))
|
|
}
|
|
}
|
|
|
|
// Update handles PATCH /api/v1/admin/engine-versions/{id}.
|
|
func (h *AdminEngineVersionsHandlers) Update() gin.HandlerFunc {
|
|
if h == nil || h.svc == nil {
|
|
return handlers.NotImplemented("adminEngineVersionsUpdate")
|
|
}
|
|
return func(c *gin.Context) {
|
|
var req engineVersionUpdateRequestWire
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
httperr.Abort(c, http.StatusBadRequest, httperr.CodeInvalidRequest, "request body must be valid JSON")
|
|
return
|
|
}
|
|
ctx := c.Request.Context()
|
|
updated, err := h.svc.Update(ctx, c.Param("id"), runtime.UpdateInput{
|
|
ImageRef: req.ImageRef,
|
|
Enabled: req.Enabled,
|
|
})
|
|
if err != nil {
|
|
respondEngineVersionError(c, h.logger, "admin engine-versions update", ctx, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, engineVersionToWire(updated))
|
|
}
|
|
}
|
|
|
|
// Disable handles POST /api/v1/admin/engine-versions/{id}/disable.
|
|
func (h *AdminEngineVersionsHandlers) Disable() gin.HandlerFunc {
|
|
if h == nil || h.svc == nil {
|
|
return handlers.NotImplemented("adminEngineVersionsDisable")
|
|
}
|
|
return func(c *gin.Context) {
|
|
ctx := c.Request.Context()
|
|
updated, err := h.svc.Disable(ctx, c.Param("id"))
|
|
if err != nil {
|
|
respondEngineVersionError(c, h.logger, "admin engine-versions disable", ctx, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, engineVersionToWire(updated))
|
|
}
|
|
}
|
|
|
|
func respondEngineVersionError(c *gin.Context, logger *zap.Logger, op string, ctx context.Context, err error) {
|
|
switch {
|
|
case errors.Is(err, runtime.ErrNotFound):
|
|
httperr.Abort(c, http.StatusNotFound, httperr.CodeNotFound, "engine version not found")
|
|
case errors.Is(err, runtime.ErrEngineVersionTaken):
|
|
httperr.Abort(c, http.StatusConflict, httperr.CodeConflict, "engine version already registered")
|
|
case errors.Is(err, runtime.ErrInvalidInput):
|
|
httperr.Abort(c, http.StatusBadRequest, httperr.CodeInvalidRequest, err.Error())
|
|
default:
|
|
logger.Error(op+" failed",
|
|
append(telemetry.TraceFieldsFromContext(ctx), zap.Error(err))...,
|
|
)
|
|
httperr.Abort(c, http.StatusInternalServerError, httperr.CodeInternalError, "internal error")
|
|
}
|
|
}
|
|
|
|
func engineVersionToWire(v runtime.EngineVersion) engineVersionWire {
|
|
return engineVersionWire{
|
|
Version: v.Version,
|
|
ImageRef: v.ImageRef,
|
|
Enabled: v.Enabled,
|
|
CreatedAt: v.CreatedAt.UTC().Format(timestampLayout),
|
|
}
|
|
}
|
|
|
|
func engineVersionListToWire(items []runtime.EngineVersion) engineVersionListWire {
|
|
out := engineVersionListWire{Items: make([]engineVersionWire, 0, len(items))}
|
|
for _, v := range items {
|
|
out.Items = append(out.Items, engineVersionToWire(v))
|
|
}
|
|
return out
|
|
}
|
|
|
|
// engineVersionWire mirrors `EngineVersion` from openapi.yaml.
|
|
type engineVersionWire struct {
|
|
Version string `json:"version"`
|
|
ImageRef string `json:"image_ref"`
|
|
Enabled bool `json:"enabled"`
|
|
CreatedAt string `json:"created_at"`
|
|
}
|
|
|
|
// engineVersionListWire mirrors `EngineVersionList`.
|
|
type engineVersionListWire struct {
|
|
Items []engineVersionWire `json:"items"`
|
|
}
|
|
|
|
// engineVersionCreateRequestWire mirrors `EngineVersionCreateRequest`.
|
|
type engineVersionCreateRequestWire struct {
|
|
Version string `json:"version"`
|
|
ImageRef string `json:"image_ref"`
|
|
Enabled *bool `json:"enabled,omitempty"`
|
|
}
|
|
|
|
// engineVersionUpdateRequestWire mirrors `EngineVersionUpdateRequest`.
|
|
type engineVersionUpdateRequestWire struct {
|
|
ImageRef *string `json:"image_ref,omitempty"`
|
|
Enabled *bool `json:"enabled,omitempty"`
|
|
}
|