feat: backend service
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
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"`
|
||||
}
|
||||
Reference in New Issue
Block a user