161 lines
4.8 KiB
Go
161 lines
4.8 KiB
Go
package cartridgecatalog
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"production/internal/dbutil"
|
|
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
)
|
|
|
|
type Handler struct {
|
|
db *pgxpool.Pool
|
|
}
|
|
|
|
func NewHandler(db *pgxpool.Pool) *Handler {
|
|
return &Handler{db: db}
|
|
}
|
|
|
|
type brandRow struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
}
|
|
|
|
type modelRow struct {
|
|
ID string `json:"id"`
|
|
BrandID string `json:"brand_id"`
|
|
ModelCode string `json:"model_code"`
|
|
}
|
|
|
|
func (h *Handler) ListBrands(c *fiber.Ctx) error {
|
|
rows, err := h.db.Query(context.Background(), `SELECT id, name FROM cartridge_brands ORDER BY name`)
|
|
if err != nil {
|
|
return c.Status(500).JSON(fiber.Map{"error": "internal error"})
|
|
}
|
|
defer rows.Close()
|
|
|
|
brands := []brandRow{}
|
|
for rows.Next() {
|
|
var b brandRow
|
|
if err := rows.Scan(&b.ID, &b.Name); err != nil {
|
|
return c.Status(500).JSON(fiber.Map{"error": "internal error"})
|
|
}
|
|
brands = append(brands, b)
|
|
}
|
|
return c.JSON(brands)
|
|
}
|
|
|
|
// CreateBrand is open to any staff role — same reasoning as
|
|
// devicecatalog.CreateBrand, a brand name carries no structural risk.
|
|
func (h *Handler) CreateBrand(c *fiber.Ctx) error {
|
|
var body struct {
|
|
Name string `json:"name"`
|
|
}
|
|
if err := c.BodyParser(&body); err != nil {
|
|
return c.Status(400).JSON(fiber.Map{"error": "invalid request body"})
|
|
}
|
|
if msg := (brandInput{Name: body.Name}).validate(); msg != "" {
|
|
return c.Status(400).JSON(fiber.Map{"error": msg})
|
|
}
|
|
name := strings.TrimSpace(body.Name)
|
|
|
|
var id string
|
|
err := h.db.QueryRow(context.Background(),
|
|
`INSERT INTO cartridge_brands (name) VALUES ($1) RETURNING id`, name,
|
|
).Scan(&id)
|
|
if err != nil {
|
|
if dbutil.IsUniqueViolation(err) {
|
|
return c.Status(409).JSON(fiber.Map{"error": "brand already exists"})
|
|
}
|
|
return c.Status(500).JSON(fiber.Map{"error": "internal error"})
|
|
}
|
|
return c.Status(201).JSON(fiber.Map{"id": id, "name": name})
|
|
}
|
|
|
|
func (h *Handler) DeleteBrand(c *fiber.Ctx) error {
|
|
id := c.Params("id")
|
|
tag, err := h.db.Exec(context.Background(), `DELETE FROM cartridge_brands WHERE id = $1::uuid`, id)
|
|
if err != nil {
|
|
if dbutil.IsFKViolation(err) {
|
|
return c.Status(409).JSON(fiber.Map{"error": "brand is used by existing models or cartridges"})
|
|
}
|
|
return c.Status(500).JSON(fiber.Map{"error": "internal error"})
|
|
}
|
|
if tag.RowsAffected() == 0 {
|
|
return c.Status(404).JSON(fiber.Map{"error": "brand not found"})
|
|
}
|
|
return c.JSON(fiber.Map{"ok": true})
|
|
}
|
|
|
|
// ListModels returns models for a brand; brand_id is required — no reason
|
|
// to ever list every model across every brand for a select-by-brand UI.
|
|
func (h *Handler) ListModels(c *fiber.Ctx) error {
|
|
brandID := c.Query("brand_id")
|
|
if brandID == "" {
|
|
return c.Status(400).JSON(fiber.Map{"error": "brand_id is required"})
|
|
}
|
|
rows, err := h.db.Query(context.Background(),
|
|
`SELECT id, brand_id, model_code FROM cartridge_models WHERE brand_id = $1::uuid ORDER BY model_code`, brandID)
|
|
if err != nil {
|
|
return c.Status(500).JSON(fiber.Map{"error": "internal error"})
|
|
}
|
|
defer rows.Close()
|
|
|
|
models := []modelRow{}
|
|
for rows.Next() {
|
|
var m modelRow
|
|
if err := rows.Scan(&m.ID, &m.BrandID, &m.ModelCode); err != nil {
|
|
return c.Status(500).JSON(fiber.Map{"error": "internal error"})
|
|
}
|
|
models = append(models, m)
|
|
}
|
|
return c.JSON(models)
|
|
}
|
|
|
|
func (h *Handler) CreateModel(c *fiber.Ctx) error {
|
|
var body struct {
|
|
BrandID string `json:"brand_id"`
|
|
ModelCode string `json:"model_code"`
|
|
}
|
|
if err := c.BodyParser(&body); err != nil {
|
|
return c.Status(400).JSON(fiber.Map{"error": "invalid request body"})
|
|
}
|
|
if msg := (modelInput{BrandID: body.BrandID, ModelCode: body.ModelCode}).validate(); msg != "" {
|
|
return c.Status(400).JSON(fiber.Map{"error": msg})
|
|
}
|
|
code := strings.TrimSpace(body.ModelCode)
|
|
|
|
var id string
|
|
err := h.db.QueryRow(context.Background(),
|
|
`INSERT INTO cartridge_models (brand_id, model_code) VALUES ($1::uuid, $2) RETURNING id`,
|
|
body.BrandID, code,
|
|
).Scan(&id)
|
|
if err != nil {
|
|
if dbutil.IsUniqueViolation(err) {
|
|
return c.Status(409).JSON(fiber.Map{"error": "model already exists for this brand"})
|
|
}
|
|
if dbutil.IsFKViolation(err) {
|
|
return c.Status(400).JSON(fiber.Map{"error": "unknown brand_id"})
|
|
}
|
|
return c.Status(500).JSON(fiber.Map{"error": "internal error"})
|
|
}
|
|
return c.Status(201).JSON(fiber.Map{"id": id, "brand_id": body.BrandID, "model_code": code})
|
|
}
|
|
|
|
func (h *Handler) DeleteModel(c *fiber.Ctx) error {
|
|
id := c.Params("id")
|
|
tag, err := h.db.Exec(context.Background(), `DELETE FROM cartridge_models WHERE id = $1::uuid`, id)
|
|
if err != nil {
|
|
if dbutil.IsFKViolation(err) {
|
|
return c.Status(409).JSON(fiber.Map{"error": "model is used by existing cartridges"})
|
|
}
|
|
return c.Status(500).JSON(fiber.Map{"error": "internal error"})
|
|
}
|
|
if tag.RowsAffected() == 0 {
|
|
return c.Status(404).JSON(fiber.Map{"error": "model not found"})
|
|
}
|
|
return c.JSON(fiber.Map{"ok": true})
|
|
}
|