Added Configuration from Item and Plate
This commit is contained in:
@@ -15,6 +15,7 @@ import (
|
||||
func NewHandler() http.Handler {
|
||||
plates := store.NewPlateStore("")
|
||||
items := store.NewItemStore("")
|
||||
configs := store.NewConfigurationStore("")
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("GET /health", health)
|
||||
@@ -26,6 +27,7 @@ func NewHandler() http.Handler {
|
||||
mux.HandleFunc("POST /items", saveItem(items))
|
||||
mux.HandleFunc("DELETE /items/{id}", deleteItem(items))
|
||||
mux.HandleFunc("GET /items/{id}/svg", serveItemSVG(items))
|
||||
registerConfigurationRoutes(mux, configs, plates, items)
|
||||
return withCORS(mux)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"printer.backend/internal/layout"
|
||||
"printer.backend/internal/model"
|
||||
"printer.backend/internal/store"
|
||||
)
|
||||
|
||||
func registerConfigurationRoutes(mux *http.ServeMux, configs *store.ConfigurationStore, plates *store.PlateStore, items *store.ItemStore) {
|
||||
mux.HandleFunc("GET /configurations", listConfigurations(configs, plates, items))
|
||||
mux.HandleFunc("POST /configurations", saveConfiguration(configs, plates, items))
|
||||
mux.HandleFunc("DELETE /configurations/{id}", deleteConfiguration(configs))
|
||||
mux.HandleFunc("GET /configurations/{id}/preview", previewConfiguration(configs, plates, items))
|
||||
mux.HandleFunc("GET /layout/preview", layoutPreview(plates, items))
|
||||
}
|
||||
|
||||
type configurationResponse struct {
|
||||
model.Configuration
|
||||
Preview *model.LayoutPreview `json:"preview,omitempty"`
|
||||
PreviewError string `json:"preview_error,omitempty"`
|
||||
}
|
||||
|
||||
func listConfigurations(configs *store.ConfigurationStore, plates *store.PlateStore, items *store.ItemStore) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, _ *http.Request) {
|
||||
list, err := configs.List()
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
if list == nil {
|
||||
list = []model.Configuration{}
|
||||
}
|
||||
|
||||
out := make([]configurationResponse, 0, len(list))
|
||||
for _, c := range list {
|
||||
out = append(out, configurationResponseFor(c, plates, items))
|
||||
}
|
||||
writeJSON(w, http.StatusOK, out)
|
||||
}
|
||||
}
|
||||
|
||||
type saveConfigurationRequest struct {
|
||||
Name string `json:"name"`
|
||||
PlateID string `json:"plate_id"`
|
||||
ItemID string `json:"item_id"`
|
||||
SpacingMM float64 `json:"spacing_mm"`
|
||||
}
|
||||
|
||||
func saveConfiguration(configs *store.ConfigurationStore, plates *store.PlateStore, items *store.ItemStore) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req saveConfigurationRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
writeError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
if req.PlateID == "" || req.ItemID == "" {
|
||||
writeError(w, http.StatusBadRequest, errors.New("plate_id and item_id are required"))
|
||||
return
|
||||
}
|
||||
if _, err := findPlate(plates, req.PlateID); err != nil {
|
||||
writeError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
if _, err := items.Get(req.ItemID); err != nil {
|
||||
writeError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
if req.SpacingMM < 0 {
|
||||
writeError(w, http.StatusBadRequest, errors.New("spacing_mm must be non-negative"))
|
||||
return
|
||||
}
|
||||
|
||||
c := model.Configuration{
|
||||
Name: req.Name,
|
||||
PlateID: req.PlateID,
|
||||
ItemID: req.ItemID,
|
||||
SpacingMM: req.SpacingMM,
|
||||
CreatedAt: time.Now().UTC(),
|
||||
}
|
||||
saved, err := configs.Save(c)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp := configurationResponseFor(saved, plates, items)
|
||||
if resp.PreviewError != "" {
|
||||
writeError(w, http.StatusInternalServerError, errors.New(resp.PreviewError))
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusCreated, resp)
|
||||
}
|
||||
}
|
||||
|
||||
func deleteConfiguration(configs *store.ConfigurationStore) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id := r.PathValue("id")
|
||||
if id == "" {
|
||||
writeError(w, http.StatusBadRequest, errors.New("id required"))
|
||||
return
|
||||
}
|
||||
if err := configs.Delete(id); err != nil {
|
||||
writeError(w, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
}
|
||||
|
||||
func previewConfiguration(configs *store.ConfigurationStore, plates *store.PlateStore, items *store.ItemStore) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id := r.PathValue("id")
|
||||
c, err := configs.Get(id)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
preview, err := buildPreview(plates, items, c.PlateID, c.ItemID, c.SpacingMM)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
preview.PlateID = c.PlateID
|
||||
preview.ItemID = c.ItemID
|
||||
writeJSON(w, http.StatusOK, preview)
|
||||
}
|
||||
}
|
||||
|
||||
func layoutPreview(plates *store.PlateStore, items *store.ItemStore) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
plateID := r.URL.Query().Get("plate_id")
|
||||
itemID := r.URL.Query().Get("item_id")
|
||||
if plateID == "" || itemID == "" {
|
||||
writeError(w, http.StatusBadRequest, errors.New("plate_id and item_id query params are required"))
|
||||
return
|
||||
}
|
||||
spacing, err := parseSpacing(r.URL.Query().Get("spacing_mm"))
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
preview, err := buildPreview(plates, items, plateID, itemID, spacing)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
preview.PlateID = plateID
|
||||
preview.ItemID = itemID
|
||||
writeJSON(w, http.StatusOK, preview)
|
||||
}
|
||||
}
|
||||
|
||||
func configurationResponseFor(c model.Configuration, plates *store.PlateStore, items *store.ItemStore) configurationResponse {
|
||||
resp := configurationResponse{Configuration: c}
|
||||
preview, err := buildPreview(plates, items, c.PlateID, c.ItemID, c.SpacingMM)
|
||||
if err != nil {
|
||||
resp.PreviewError = err.Error()
|
||||
return resp
|
||||
}
|
||||
preview.PlateID = c.PlateID
|
||||
preview.ItemID = c.ItemID
|
||||
resp.Preview = &preview
|
||||
return resp
|
||||
}
|
||||
|
||||
func buildPreview(plates *store.PlateStore, items *store.ItemStore, plateID, itemID string, spacingMM float64) (model.LayoutPreview, error) {
|
||||
plate, err := findPlate(plates, plateID)
|
||||
if err != nil {
|
||||
return model.LayoutPreview{}, err
|
||||
}
|
||||
item, err := items.Get(itemID)
|
||||
if err != nil {
|
||||
return model.LayoutPreview{}, err
|
||||
}
|
||||
return layout.Pack(plate, item.Spec, spacingMM), nil
|
||||
}
|
||||
|
||||
func findPlate(plates *store.PlateStore, id string) (model.Plate, error) {
|
||||
list, err := plates.List()
|
||||
if err != nil {
|
||||
return model.Plate{}, err
|
||||
}
|
||||
plate, err := storeFindPlate(list, id)
|
||||
if err != nil {
|
||||
return model.Plate{}, errors.New("plate not found: " + id)
|
||||
}
|
||||
return plate, nil
|
||||
}
|
||||
|
||||
func storeFindPlate(list []model.Plate, id string) (model.Plate, error) {
|
||||
for _, p := range list {
|
||||
if p.ID == id {
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
return model.Plate{}, errors.New("not found")
|
||||
}
|
||||
|
||||
func parseSpacing(s string) (float64, error) {
|
||||
if s == "" {
|
||||
return 0, nil
|
||||
}
|
||||
v, err := strconv.ParseFloat(s, 64)
|
||||
if err != nil {
|
||||
return 0, errors.New("invalid spacing_mm")
|
||||
}
|
||||
if v < 0 {
|
||||
return 0, errors.New("spacing_mm must be non-negative")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
Reference in New Issue
Block a user