Public source validation / validate (push) Failing after 3m8s
69 lines
2.7 KiB
Go
69 lines
2.7 KiB
Go
// Package widgetapi exposes the bounded widget catalog and preview contract.
|
|
package widgetapi
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/itworx/pulse/internal/problem"
|
|
"github.com/itworx/pulse/internal/widget"
|
|
"github.com/itworx/pulse/internal/widgetpreview"
|
|
)
|
|
|
|
type Handler struct{ Registry widget.Registry }
|
|
|
|
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
if err := r.Context().Err(); err != nil {
|
|
return
|
|
}
|
|
switch {
|
|
case r.URL.Path == "/api/v1/widgets/catalog" && r.Method == http.MethodGet:
|
|
write(w, http.StatusOK, map[string]any{"items": h.Registry.Catalog()})
|
|
case r.URL.Path == "/api/v1/widgets/preview" && r.Method == http.MethodPost:
|
|
contentType := strings.ToLower(strings.TrimSpace(strings.Split(r.Header.Get("Content-Type"), ";")[0]))
|
|
if contentType != "" && contentType != "application/json" {
|
|
problem.Write(w, r, http.StatusUnsupportedMediaType, "UNSUPPORTED_MEDIA_TYPE", "Unsupported media type", "Widget previews require application/json.", nil)
|
|
return
|
|
}
|
|
var body struct {
|
|
Widget map[string]any `json:"widget"`
|
|
State string `json:"state"`
|
|
}
|
|
decoder := json.NewDecoder(http.MaxBytesReader(w, r.Body, 1<<20))
|
|
decoder.DisallowUnknownFields()
|
|
if err := decoder.Decode(&body); err != nil || body.Widget == nil {
|
|
problem.Write(w, r, http.StatusBadRequest, "INVALID_PREVIEW", "Invalid preview", "A bounded widget configuration is required.", nil)
|
|
return
|
|
}
|
|
var trailing any
|
|
if err := decoder.Decode(&trailing); !errors.Is(err, io.EOF) {
|
|
problem.Write(w, r, http.StatusBadRequest, "INVALID_PREVIEW", "Invalid preview", "The request must contain one JSON document.", nil)
|
|
return
|
|
}
|
|
preview, err := widgetpreview.Preview(body.Widget, body.State)
|
|
if err != nil {
|
|
var invalid widgetpreview.InvalidConfig
|
|
if errors.As(err, &invalid) {
|
|
problem.Write(w, r, http.StatusBadRequest, "INVALID_WIDGET_CONFIG", "Invalid widget", "The widget configuration is invalid.", invalid.Fields)
|
|
return
|
|
}
|
|
problem.Write(w, r, http.StatusBadRequest, "INVALID_WIDGET_CONFIG", "Invalid widget", "The widget configuration is invalid.", nil)
|
|
return
|
|
}
|
|
write(w, http.StatusOK, map[string]any{"preview": preview})
|
|
case r.URL.Path == "/api/v1/widgets/catalog" || r.URL.Path == "/api/v1/widgets/preview":
|
|
problem.Write(w, r, http.StatusMethodNotAllowed, "METHOD_NOT_ALLOWED", "Method not allowed", "The requested method is not supported for this widget route.", nil)
|
|
default:
|
|
problem.Write(w, r, http.StatusNotFound, "NOT_FOUND", "Not found", "The requested widget route does not exist.", nil)
|
|
}
|
|
}
|
|
|
|
func write(w http.ResponseWriter, status int, value any) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(status)
|
|
_ = json.NewEncoder(w).Encode(value)
|
|
}
|