Files
descrybe/apps/api/internal/httpapi/standard_fields_handlers.go
T
greeneclipse 8580c996c3 Initial commit of Descrybe v2 without local scratch artifacts.
Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
2026-08-09 22:47:43 +02:00

284 lines
8.6 KiB
Go

package httpapi
import (
"errors"
"net/http"
"strings"
"github.com/descrybe/descrybe-v2/apps/api/internal/catalog"
"github.com/go-chi/chi/v5"
"github.com/google/uuid"
)
func (s *Server) handleListFieldGroups(w http.ResponseWriter, r *http.Request) {
cid, _ := CompanyIDFromContext(r.Context())
items, err := s.Catalog.ListFieldGroups(r.Context(), cid)
if err != nil {
Error(w, http.StatusInternalServerError, "list failed")
return
}
limit, offset := ParseLimitOffsetMax(r, maxTreePageLimit)
page, total := pageSlice(items, limit, offset)
JSON(w, http.StatusOK, map[string]any{"groups": page, "total": total, "limit": limit, "offset": offset})
}
func (s *Server) handleCreateFieldGroup(w http.ResponseWriter, r *http.Request) {
cid, _ := CompanyIDFromContext(r.Context())
var body map[string]any
if err := DecodeJSON(r, &body); err != nil {
Error(w, http.StatusBadRequest, "invalid json")
return
}
name, _ := body["name"].(string)
var desc *string
if v, ok := body["description"]; ok {
if v == nil {
empty := ""
desc = &empty
} else if str, ok := v.(string); ok {
desc = &str
}
}
order := 0
if v, ok := body["order"].(float64); ok {
order = int(v)
}
item, err := s.Catalog.CreateFieldGroup(r.Context(), cid, name, desc, order)
if err != nil {
ClientOrLog(w, http.StatusBadRequest, "could not create field group", err, catalog.ClientError)
return
}
JSON(w, http.StatusCreated, item)
}
func (s *Server) handleUpdateFieldGroup(w http.ResponseWriter, r *http.Request) {
cid, _ := CompanyIDFromContext(r.Context())
id, err := uuid.Parse(chi.URLParam(r, "id"))
if err != nil {
Error(w, http.StatusBadRequest, "invalid id")
return
}
var body map[string]any
if err := DecodeJSON(r, &body); err != nil {
Error(w, http.StatusBadRequest, "invalid json")
return
}
item, err := s.Catalog.UpdateFieldGroup(r.Context(), cid, id, body)
if errors.Is(err, catalog.ErrNotFound) {
Error(w, http.StatusNotFound, "not found")
return
}
if errors.Is(err, catalog.ErrSystemImmutable) {
Error(w, http.StatusForbidden, err.Error())
return
}
if err != nil {
ClientOrLog(w, http.StatusBadRequest, "could not update field group", err, catalog.ClientError)
return
}
JSON(w, http.StatusOK, item)
}
func (s *Server) handleDeleteFieldGroup(w http.ResponseWriter, r *http.Request) {
cid, _ := CompanyIDFromContext(r.Context())
id, err := uuid.Parse(chi.URLParam(r, "id"))
if err != nil {
Error(w, http.StatusBadRequest, "invalid id")
return
}
err = s.Catalog.DeleteFieldGroup(r.Context(), cid, id)
if errors.Is(err, catalog.ErrNotFound) {
Error(w, http.StatusNotFound, "not found")
return
}
if errors.Is(err, catalog.ErrSystemImmutable) {
Error(w, http.StatusForbidden, err.Error())
return
}
if err != nil {
Error(w, http.StatusInternalServerError, "delete failed")
return
}
JSON(w, http.StatusOK, map[string]string{"status": "ok"})
}
func (s *Server) handleListStandardFields(w http.ResponseWriter, r *http.Request) {
cid, _ := CompanyIDFromContext(r.Context())
enabledOnly := false
switch strings.ToLower(strings.TrimSpace(r.URL.Query().Get("enabled"))) {
case "1", "true", "yes":
enabledOnly = true
}
items, err := s.Catalog.ListStandardFields(r.Context(), cid, enabledOnly)
if err != nil {
Error(w, http.StatusInternalServerError, "list failed")
return
}
limit, offset := ParseLimitOffsetMax(r, maxTreePageLimit)
page, total := pageSlice(items, limit, offset)
JSON(w, http.StatusOK, map[string]any{"fields": page, "total": total, "limit": limit, "offset": offset})
}
func (s *Server) handleCreateStandardField(w http.ResponseWriter, r *http.Request) {
cid, _ := CompanyIDFromContext(r.Context())
var body map[string]any
if err := DecodeJSON(r, &body); err != nil {
Error(w, http.StatusBadRequest, "invalid json")
return
}
item, err := s.Catalog.CreateStandardField(r.Context(), cid, body)
if err != nil {
ClientOrLog(w, http.StatusBadRequest, "could not create standard field", err, catalog.ClientError)
return
}
JSON(w, http.StatusCreated, item)
}
func (s *Server) handleUpdateStandardField(w http.ResponseWriter, r *http.Request) {
cid, _ := CompanyIDFromContext(r.Context())
id, err := uuid.Parse(chi.URLParam(r, "id"))
if err != nil {
Error(w, http.StatusBadRequest, "invalid id")
return
}
var body map[string]any
if err := DecodeJSON(r, &body); err != nil {
Error(w, http.StatusBadRequest, "invalid json")
return
}
item, err := s.Catalog.UpdateStandardField(r.Context(), cid, id, body)
if errors.Is(err, catalog.ErrNotFound) {
Error(w, http.StatusNotFound, "not found")
return
}
if errors.Is(err, catalog.ErrSystemImmutable) {
Error(w, http.StatusForbidden, err.Error())
return
}
if err != nil {
ClientOrLog(w, http.StatusBadRequest, "could not update standard field", err, catalog.ClientError)
return
}
JSON(w, http.StatusOK, item)
}
func (s *Server) handleDeleteStandardField(w http.ResponseWriter, r *http.Request) {
cid, _ := CompanyIDFromContext(r.Context())
id, err := uuid.Parse(chi.URLParam(r, "id"))
if err != nil {
Error(w, http.StatusBadRequest, "invalid id")
return
}
err = s.Catalog.DeleteStandardField(r.Context(), cid, id)
if errors.Is(err, catalog.ErrNotFound) {
Error(w, http.StatusNotFound, "not found")
return
}
if errors.Is(err, catalog.ErrSystemImmutable) {
Error(w, http.StatusForbidden, err.Error())
return
}
if err != nil {
Error(w, http.StatusInternalServerError, "delete failed")
return
}
JSON(w, http.StatusOK, map[string]string{"status": "ok"})
}
func (s *Server) handleBulkStandardFieldsEnabled(w http.ResponseWriter, r *http.Request) {
cid, _ := CompanyIDFromContext(r.Context())
var body struct {
IDs []string `json:"ids"`
Enabled *bool `json:"enabled"`
}
if err := DecodeJSON(r, &body); err != nil {
Error(w, http.StatusBadRequest, "invalid json")
return
}
if body.Enabled == nil {
Error(w, http.StatusBadRequest, "enabled required")
return
}
ids := make([]uuid.UUID, 0, len(body.IDs))
for _, raw := range body.IDs {
id, err := uuid.Parse(strings.TrimSpace(raw))
if err != nil {
Error(w, http.StatusBadRequest, "invalid id")
return
}
ids = append(ids, id)
}
n, err := s.Catalog.BulkSetStandardFieldsEnabled(r.Context(), cid, ids, *body.Enabled)
if err != nil {
ClientOrLog(w, http.StatusBadRequest, "could not update standard fields", err, catalog.ClientError)
return
}
JSON(w, http.StatusOK, map[string]any{"updated": n, "enabled": *body.Enabled})
}
func (s *Server) handleEnableRecommendedStandardFields(w http.ResponseWriter, r *http.Request) {
cid, _ := CompanyIDFromContext(r.Context())
items, err := s.Catalog.EnableRecommendedEcommerce(r.Context(), cid)
if err != nil {
LogAndError(w, http.StatusInternalServerError, "enable recommended fields failed", err)
return
}
JSON(w, http.StatusOK, map[string]any{"fields": items, "status": "ok"})
}
func (s *Server) handleListStructuredDescriptions(w http.ResponseWriter, r *http.Request) {
cid, _ := CompanyIDFromContext(r.Context())
items, err := s.Catalog.ListStructuredDescriptions(r.Context(), cid)
if err != nil {
Error(w, http.StatusInternalServerError, "list failed")
return
}
limit, offset := ParseLimitOffsetMax(r, maxTreePageLimit)
page, total := pageSlice(items, limit, offset)
JSON(w, http.StatusOK, map[string]any{"fields": page, "total": total, "limit": limit, "offset": offset})
}
func (s *Server) handleCreateStructuredDescription(w http.ResponseWriter, r *http.Request) {
cid, _ := CompanyIDFromContext(r.Context())
var body map[string]any
if err := DecodeJSON(r, &body); err != nil {
Error(w, http.StatusBadRequest, "invalid json")
return
}
fieldKey := ""
if v, ok := body["field_key"].(string); ok {
fieldKey = v
} else if v, ok := body["fieldKey"].(string); ok {
fieldKey = v
}
typ := "text"
if v, ok := body["type"].(string); ok && v != "" {
typ = v
}
item, err := s.Catalog.CreateStructuredDescription(r.Context(), cid, fieldKey, typ)
if err != nil {
ClientOrLog(w, http.StatusBadRequest, "could not create structured description", err, catalog.ClientError)
return
}
JSON(w, http.StatusCreated, item)
}
func (s *Server) handleDeleteStructuredDescription(w http.ResponseWriter, r *http.Request) {
cid, _ := CompanyIDFromContext(r.Context())
id, err := uuid.Parse(chi.URLParam(r, "id"))
if err != nil {
Error(w, http.StatusBadRequest, "invalid id")
return
}
err = s.Catalog.DeleteStructuredDescription(r.Context(), cid, id)
if errors.Is(err, catalog.ErrNotFound) {
Error(w, http.StatusNotFound, "not found")
return
}
if err != nil {
Error(w, http.StatusInternalServerError, "delete failed")
return
}
JSON(w, http.StatusOK, map[string]string{"status": "ok"})
}