Files
Gardomatic/internal/storage/postgres/task_template_opt_outs.go
T
kleiax 904d14b64c
CI / test (push) Canceled after 0s
Initial commit
2026-09-12 22:22:17 +02:00

61 lines
2.3 KiB
Go

package postgres
import (
"database/sql"
"gardomatic.kleiax.de/internal/storage"
)
// TaskTemplateOptOutModel stores per-plant task-generation suppression while
// enforcing the garden boundary.
type TaskTemplateOptOutModel struct{ DB *sql.DB }
// IsOptedOut reports whether task generation is suppressed for a plant-template pair.
func (m TaskTemplateOptOutModel) IsOptedOut(gardenID, plantID, templateID int) (bool, error) {
ctx, cancel := contextWithTimeout()
defer cancel()
var value bool
err := m.DB.QueryRowContext(ctx, `SELECT EXISTS(SELECT 1 FROM task_template_opt_outs o JOIN plants p ON p.id=o.plant_id JOIN species_task_templates st ON st.id=o.template_id WHERE p.garden_id=$1 AND p.id=$2 AND st.id=$3 AND st.species_id=p.species_id)`, gardenID, plantID, templateID).Scan(&value)
return value, err
}
// GetAllForPlant lists suppressed template IDs for a plant.
func (m TaskTemplateOptOutModel) GetAllForPlant(gardenID, plantID int) ([]int, error) {
ctx, cancel := contextWithTimeout()
defer cancel()
rows, err := m.DB.QueryContext(ctx, `SELECT o.template_id FROM task_template_opt_outs o JOIN plants p ON p.id=o.plant_id WHERE p.garden_id=$1 AND p.id=$2 ORDER BY o.template_id`, gardenID, plantID)
if err != nil {
return nil, err
}
defer rows.Close()
result := []int{}
for rows.Next() {
var id int
if err = rows.Scan(&id); err != nil {
return nil, err
}
result = append(result, id)
}
return result, rows.Err()
}
// Set creates or removes suppression for a plant-template pair.
func (m TaskTemplateOptOutModel) Set(gardenID, plantID, templateID int, optedOut bool) error {
ctx, cancel := contextWithTimeout()
defer cancel()
var valid bool
err := m.DB.QueryRowContext(ctx, `SELECT EXISTS(SELECT 1 FROM plants p JOIN species_task_templates st ON st.species_id=p.species_id WHERE p.garden_id=$1 AND p.id=$2 AND st.id=$3)`, gardenID, plantID, templateID).Scan(&valid)
if err != nil {
return err
}
if !valid {
return storage.ErrRecordNotFound
}
if optedOut {
_, err = m.DB.ExecContext(ctx, `INSERT INTO task_template_opt_outs(plant_id,template_id) VALUES($1,$2) ON CONFLICT DO NOTHING`, plantID, templateID)
} else {
_, err = m.DB.ExecContext(ctx, `DELETE FROM task_template_opt_outs WHERE plant_id=$1 AND template_id=$2`, plantID, templateID)
}
return err
}