package httpapi import ( "context" "fmt" "github.com/dtoro/oikos/internal/db/sqlcgen" "github.com/dtoro/oikos/internal/domain" "github.com/dtoro/oikos/internal/httpapi/gen" "github.com/dtoro/oikos/internal/observability" ) // ─── Autonomy Settings ───────────────────────────────────────────────── func (s *Server) GetAutonomySettings(ctx context.Context, req gen.GetAutonomySettingsRequestObject) (gen.GetAutonomySettingsResponseObject, error) { rows, err := s.pool.Query(ctx, `SELECT key, value, version, updated_at FROM autonomy_settings ORDER BY key`) if err != nil { return nil, err } defer rows.Close() items := []gen.AutonomySetting{} for rows.Next() { var as gen.AutonomySetting if err := rows.Scan(&as.Key, &as.Value, &as.Version, &as.UpdatedAt); err != nil { return nil, err } items = append(items, as) } if rows.Err() != nil { return nil, rows.Err() } if items == nil { items = []gen.AutonomySetting{} } return gen.GetAutonomySettings200JSONResponse{Items: items}, nil } func (s *Server) PatchAutonomySettings(ctx context.Context, req gen.PatchAutonomySettingsRequestObject) (gen.PatchAutonomySettingsResponseObject, error) { if req.Body == nil { return nil, fmt.Errorf("%w: request body is required", domain.ErrInvalidInput) } tx, err := s.pool.Begin(ctx) if err != nil { return nil, err } defer tx.Rollback(ctx) for key, value := range *req.Body { _, err := tx.Exec(ctx, ` INSERT INTO autonomy_settings (key, value, version, updated_at) VALUES ($1, $2, 1, now()) ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, version = autonomy_settings.version + 1, updated_at = now()`, key, value) if err != nil { return nil, err } } // Re-read all settings. rows, err := tx.Query(ctx, `SELECT key, value, version, updated_at FROM autonomy_settings ORDER BY key`) if err != nil { return nil, err } defer rows.Close() items := []gen.AutonomySetting{} for rows.Next() { var as gen.AutonomySetting if err := rows.Scan(&as.Key, &as.Value, &as.Version, &as.UpdatedAt); err != nil { return nil, err } items = append(items, as) } if rows.Err() != nil { return nil, rows.Err() } actorType, actor := actorInfo(ctx) if auditErr := observability.Audit(ctx, sqlcgen.New(tx), actorType, actor, "patch", nil, "PATCH", "/api/v1/policy/autonomy", "", nil, map[string]any{"keys": keysOfMap(*req.Body)}); auditErr != nil { return nil, auditErr } if err := tx.Commit(ctx); err != nil { return nil, err } return gen.PatchAutonomySettings200JSONResponse{Items: items}, nil } // keysOfMap returns the keys of a map[string]string. func keysOfMap(m map[string]string) []string { keys := make([]string, 0, len(m)) for k := range m { keys = append(keys, k) } return keys }