teg/backend/device_lookups.go
2026-05-19 18:07:22 +02:00

188 lines
3.7 KiB
Go

// backend\device_lookups.go
package main
import (
"context"
"fmt"
"net/http"
"strings"
"github.com/jackc/pgx/v5"
)
type DeviceLookupOption struct {
ID string `json:"id"`
Name string `json:"name"`
}
type CreateDeviceLookupRequest struct {
Name string `json:"name"`
}
func (s *Server) handleDeviceLookups(w http.ResponseWriter, r *http.Request) {
manufacturers, err := listDeviceLookupOptions(r.Context(), s.db, "device_manufacturers")
if err != nil {
writeError(w, http.StatusInternalServerError, "Hersteller konnten nicht geladen werden")
return
}
locations, err := listDeviceLookupOptions(r.Context(), s.db, "device_locations")
if err != nil {
writeError(w, http.StatusInternalServerError, "Standorte konnten nicht geladen werden")
return
}
writeJSON(w, http.StatusOK, map[string]any{
"manufacturers": manufacturers,
"locations": locations,
})
}
func (s *Server) handleCreateDeviceManufacturer(w http.ResponseWriter, r *http.Request) {
s.handleCreateDeviceLookup(w, r, "device_manufacturers", "Hersteller")
}
func (s *Server) handleCreateDeviceLocation(w http.ResponseWriter, r *http.Request) {
s.handleCreateDeviceLookup(w, r, "device_locations", "Standort")
}
func (s *Server) handleCreateDeviceLookup(
w http.ResponseWriter,
r *http.Request,
tableName string,
label string,
) {
var input CreateDeviceLookupRequest
if err := readJSON(r, &input); err != nil {
writeError(w, http.StatusBadRequest, "Invalid JSON")
return
}
name := strings.TrimSpace(input.Name)
if name == "" {
writeError(w, http.StatusBadRequest, label+" ist erforderlich")
return
}
option, err := upsertDeviceLookupOption(r.Context(), s.db, tableName, name)
if err != nil {
writeError(w, http.StatusInternalServerError, label+" konnte nicht gespeichert werden")
return
}
writeJSON(w, http.StatusCreated, map[string]any{
"option": option,
})
}
func listDeviceLookupOptions(
ctx context.Context,
db interface {
Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error)
},
tableName string,
) ([]DeviceLookupOption, error) {
query := fmt.Sprintf(
`
SELECT id::TEXT, name
FROM %s
ORDER BY lower(name) ASC
`,
tableName,
)
rows, err := db.Query(ctx, query)
if err != nil {
return nil, err
}
defer rows.Close()
options := []DeviceLookupOption{}
for rows.Next() {
var option DeviceLookupOption
if err := rows.Scan(&option.ID, &option.Name); err != nil {
return nil, err
}
options = append(options, option)
}
if err := rows.Err(); err != nil {
return nil, err
}
return options, nil
}
func ensureDeviceLookupValues(
ctx context.Context,
tx pgx.Tx,
manufacturer string,
location string,
) error {
if err := ensureOptionalDeviceLookupValue(ctx, tx, "device_manufacturers", manufacturer); err != nil {
return err
}
if err := ensureOptionalDeviceLookupValue(ctx, tx, "device_locations", location); err != nil {
return err
}
return nil
}
func ensureOptionalDeviceLookupValue(
ctx context.Context,
db deviceTransaction,
tableName string,
name string,
) error {
name = strings.TrimSpace(name)
if name == "" {
return nil
}
_, err := upsertDeviceLookupOption(ctx, db, tableName, name)
return err
}
func upsertDeviceLookupOption(
ctx context.Context,
db deviceTransaction,
tableName string,
name string,
) (DeviceLookupOption, error) {
name = strings.TrimSpace(name)
normalizedName := strings.ToLower(name)
query := fmt.Sprintf(
`
INSERT INTO %s (
name,
normalized_name
)
VALUES ($1, $2)
ON CONFLICT (normalized_name)
DO UPDATE SET
updated_at = now()
RETURNING id::TEXT, name
`,
tableName,
)
var option DeviceLookupOption
err := db.QueryRow(
ctx,
query,
name,
normalizedName,
).Scan(&option.ID, &option.Name)
return option, err
}