Files
query-orchestration/internal/uisettings/service.go
T
Jacob Mathison 8a4029058b Merged in jmathison/demo-table (pull request #213)
Add demo dashboard settings table and CRUD API

* Add demo dashboard settings table and CRUD API

- Migration 00000000000123: create demoDashboardSettings table (id, guid, key, value JSONB, isDeleted, timestamps)
- SQLC queries and repository for create, list, listByGuid, get, update, soft delete
- OpenAPI paths and schemas in queryAPI.yaml; regenerate api.gen.go (embedded spec)
- Handlers in api/queryAPI/demodashboardsettings.go; ConfigProvider GetDBQueries for repo access
- Unit tests for demo dashboard settings CRUD

* Fix sqlc-generated demo dashboard setting types

Align demo dashboard settings handlers with sqlc output (DemoDashboardSetting model + pointer slices) so CI codegen/build succeeds.

* Fix CI gosec and gofmt issues

- Suppress gosec G115 for TotalCount len->int32 cast (consistent with other list endpoints)
- gofmt mock GetDBQueries() in test helpers

* gofmt query API demo settings

* Tighten demo dashboard settings OpenAPI constraints

* Regenerate query API embedded spec

* refactor: generic UI settings (table, API, service) and three-layer architecture

- Replace demo-dashboard-settings with generic UI settings:
  - Table uiSettings with namespace (was guid), key, value JSONB; UNIQUE(namespace, key)
  - Migration 123: create_ui_settings (single migration, no rename)
  - OpenAPI: /ui-settings, UISettingsService, UISetting schemas with namespace
  - API types and handlers: CreateUISetting, ListUISettings, GetUISetting, UpdateUISetting, DeleteUISetting

- Add service layer (architecture fix):
  - internal/uisettings: Service with Create, List, ListByNamespace, Get, Update, SoftDelete
  - Controllers call s.svc.UISettings only; remove GetDBQueries from queryAPI ConfigProvider
  - Wire UISettings in Services, main.go, and testutils

- Repository: uisettings.sql + uisettings.sql.go, UISetting model; remove demodashboardsettings
- Controller: uisettings.go (replaces demodashboardsettings.go), ErrNotFound from uisettings package
- Tests: uisettings_test.go with namespace-based list/cre…
* Add UISetting model and generated queries for uiSettings table

- models.go: add UISetting struct (sqlc 1.30.0)
- uisettings.sql.go: generated CRUD for ui_settings feature

Made-with: Cursor

* Use repository.UiSetting to match sqlc-generated type

- service and API use repository.UiSetting (sqlc struct name for uiSettings table)
- uisettings.sql.go: keep CreateUISetting/GetUISetting etc. from query names, return *UiSetting
- Fixes Docker/CI build undefined repository.UISetting

* Fix import formatting for golangci-lint

* Lint fix

* uisettings list test

* tests fixes

* Merge main: resolve api.gen.go conflicts (UI settings + DeleteDocument)

* Doc updates and elimination of index redundancy


Approved-by: Jay Brown
2026-03-04 23:00:50 +00:00

97 lines
2.9 KiB
Go

// Package uisettings provides generic UI key-value settings storage (e.g. demo dashboard state,
// feature flags, saved filters). Callers partition data via namespace (e.g. "demo-dashboard:{userId}").
// It handles CRUD and soft-delete semantics; value is stored as JSON.
package uisettings
import (
"context"
"errors"
"fmt"
"queryorchestration/internal/database/repository"
"queryorchestration/internal/serviceconfig"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
)
// Service provides UI settings operations.
type Service struct {
cfg serviceconfig.ConfigProvider
}
// New creates a new UI settings service.
func New(cfg serviceconfig.ConfigProvider) *Service {
return &Service{cfg: cfg}
}
// Create creates a new UI setting.
func (s *Service) Create(ctx context.Context, namespace, key string, value []byte) (*repository.UiSetting, error) {
row, err := s.cfg.GetDBQueries().CreateUISetting(ctx, &repository.CreateUISettingParams{
Namespace: namespace,
Key: key,
Value: value,
})
if err != nil {
return nil, fmt.Errorf("create UI setting: %w", err)
}
return row, nil
}
// List returns all non-deleted UI settings.
func (s *Service) List(ctx context.Context) ([]*repository.UiSetting, error) {
rows, err := s.cfg.GetDBQueries().ListUISettings(ctx)
if err != nil {
return nil, fmt.Errorf("list UI settings: %w", err)
}
return rows, nil
}
// ListByNamespace returns non-deleted UI settings for the given namespace.
func (s *Service) ListByNamespace(ctx context.Context, namespace string) ([]*repository.UiSetting, error) {
rows, err := s.cfg.GetDBQueries().ListUISettingsByNamespace(ctx, namespace)
if err != nil {
return nil, fmt.Errorf("list UI settings by namespace: %w", err)
}
return rows, nil
}
// Get returns a single setting by id, or ErrNotFound if not found or soft-deleted.
func (s *Service) Get(ctx context.Context, id uuid.UUID) (*repository.UiSetting, error) {
row, err := s.cfg.GetDBQueries().GetUISetting(ctx, id)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return nil, ErrNotFound
}
return nil, fmt.Errorf("get UI setting: %w", err)
}
if row == nil {
return nil, ErrNotFound
}
return row, nil
}
// Update updates a setting's value. Returns ErrNotFound if the setting does not exist or is soft-deleted.
func (s *Service) Update(ctx context.Context, id uuid.UUID, value []byte) error {
err := s.cfg.GetDBQueries().UpdateUISetting(ctx, &repository.UpdateUISettingParams{
ID: id,
Value: value,
})
if err != nil {
return fmt.Errorf("update UI setting: %w", err)
}
return nil
}
// SoftDelete soft-deletes a setting by id.
func (s *Service) SoftDelete(ctx context.Context, id uuid.UUID) error {
err := s.cfg.GetDBQueries().SoftDeleteUISetting(ctx, id)
if err != nil {
return fmt.Errorf("soft delete UI setting: %w", err)
}
return nil
}
// ErrNotFound is returned when a setting is not found or is soft-deleted.
var ErrNotFound = errors.New("UI setting not found")