125 lines
6.0 KiB
Go
125 lines
6.0 KiB
Go
package postgres
|
|
|
|
import (
|
|
"database/sql"
|
|
|
|
"gardomatic.kleiax.de/internal/storage"
|
|
)
|
|
|
|
// SpeciesTaskTemplateModel stores recurring species task rules in PostgreSQL.
|
|
// SpeciesTaskTemplateModel implements storage.SpeciesTaskTemplateModelInterface
|
|
// and resolves global species through the requesting garden boundary.
|
|
type SpeciesTaskTemplateModel struct{ DB *sql.DB }
|
|
|
|
const speciesTaskTemplateColumns = `t.id, t.species_id, t.origin, t.title, t.description, t.trigger_type,
|
|
t.month_from, t.day_from, t.month_to, t.day_to, t.offset_days_from, t.offset_days_to,
|
|
t.interval_days, t.trigger_offset, t.trigger_offset_unit, t.duration, t.duration_unit,
|
|
t.recurrence, t.recurrence_interval, t.priority, t.active, t.created_at, t.updated_at, t.version`
|
|
|
|
func scanSpeciesTaskTemplate(s scanner) (storage.SpeciesTaskTemplate, error) {
|
|
var template storage.SpeciesTaskTemplate
|
|
err := s.Scan(
|
|
&template.ID, &template.SpeciesID, &template.Origin, &template.Title, &template.Description,
|
|
&template.TriggerType, &template.MonthFrom, &template.DayFrom, &template.MonthTo,
|
|
&template.DayTo, &template.OffsetDaysFrom, &template.OffsetDaysTo,
|
|
&template.IntervalDays, &template.TriggerOffset, &template.TriggerOffsetUnit, &template.Duration, &template.DurationUnit,
|
|
&template.Recurrence, &template.RecurrenceInterval, &template.Priority, &template.Active, &template.CreatedAt,
|
|
&template.UpdatedAt, &template.Version,
|
|
)
|
|
return template, err
|
|
}
|
|
|
|
// Insert creates a task template for a species.
|
|
func (m SpeciesTaskTemplateModel) Insert(template storage.SpeciesTaskTemplate) (storage.SpeciesTaskTemplate, error) {
|
|
ctx, cancel := contextWithTimeout()
|
|
defer cancel()
|
|
err := m.DB.QueryRowContext(ctx, `
|
|
INSERT INTO species_task_templates (
|
|
species_id, origin, title, description, trigger_type, month_from, day_from,
|
|
month_to, day_to, offset_days_from, offset_days_to, interval_days,
|
|
trigger_offset, trigger_offset_unit, duration, duration_unit, recurrence, recurrence_interval, priority, active)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20)
|
|
RETURNING id, created_at, updated_at, version`,
|
|
template.SpeciesID, template.Origin, template.Title, template.Description, template.TriggerType,
|
|
template.MonthFrom, template.DayFrom, template.MonthTo, template.DayTo,
|
|
template.OffsetDaysFrom, template.OffsetDaysTo, template.IntervalDays,
|
|
template.TriggerOffset, template.TriggerOffsetUnit, template.Duration, template.DurationUnit,
|
|
template.Recurrence, template.RecurrenceInterval, template.Priority, template.Active,
|
|
).Scan(&template.ID, &template.CreatedAt, &template.UpdatedAt, &template.Version)
|
|
if err != nil {
|
|
return storage.SpeciesTaskTemplate{}, err
|
|
}
|
|
return template, nil
|
|
}
|
|
|
|
// Get returns a task template visible in a garden.
|
|
func (m SpeciesTaskTemplateModel) Get(gardenID, id int) (storage.SpeciesTaskTemplate, error) {
|
|
ctx, cancel := contextWithTimeout()
|
|
defer cancel()
|
|
template, err := scanSpeciesTaskTemplate(m.DB.QueryRowContext(ctx,
|
|
`SELECT `+speciesTaskTemplateColumns+` FROM species_task_templates t JOIN species s ON s.id = t.species_id WHERE (s.garden_id IS NULL OR s.garden_id = $1) AND t.id = $2`, gardenID, id))
|
|
if err != nil {
|
|
return storage.SpeciesTaskTemplate{}, recordError(err)
|
|
}
|
|
return template, nil
|
|
}
|
|
|
|
// GetAllForSpecies lists task templates for a species visible in a garden.
|
|
func (m SpeciesTaskTemplateModel) GetAllForSpecies(gardenID, speciesID int) ([]storage.SpeciesTaskTemplate, error) {
|
|
ctx, cancel := contextWithTimeout()
|
|
defer cancel()
|
|
rows, err := m.DB.QueryContext(ctx, `SELECT `+speciesTaskTemplateColumns+`
|
|
FROM species_task_templates t JOIN species s ON s.id = t.species_id
|
|
WHERE (s.garden_id IS NULL OR s.garden_id = $1) AND t.species_id = $2
|
|
ORDER BY t.active DESC, t.priority DESC, t.title, t.id`, gardenID, speciesID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
templates := []storage.SpeciesTaskTemplate{}
|
|
for rows.Next() {
|
|
template, err := scanSpeciesTaskTemplate(rows)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
templates = append(templates, template)
|
|
}
|
|
return templates, rows.Err()
|
|
}
|
|
|
|
// Update changes a task template using optimistic locking.
|
|
func (m SpeciesTaskTemplateModel) Update(gardenID int, template storage.SpeciesTaskTemplate) (storage.SpeciesTaskTemplate, error) {
|
|
ctx, cancel := contextWithTimeout()
|
|
defer cancel()
|
|
err := m.DB.QueryRowContext(ctx, `
|
|
UPDATE species_task_templates SET species_id = $1, origin = $2, title = $3, description = $4,
|
|
trigger_type = $5, month_from = $6, day_from = $7, month_to = $8,
|
|
day_to = $9, offset_days_from = $10, offset_days_to = $11,
|
|
interval_days = $12, trigger_offset = $13, trigger_offset_unit = $14,
|
|
duration = $15, duration_unit = $16, recurrence = $17, recurrence_interval = $18,
|
|
priority = $19, active = $20,
|
|
updated_at = CURRENT_TIMESTAMP, version = version + 1
|
|
WHERE id = $21 AND version = $22
|
|
AND EXISTS (SELECT 1 FROM species s WHERE s.id = species_task_templates.species_id AND s.garden_id IS NOT DISTINCT FROM NULLIF($23, 0))
|
|
RETURNING updated_at, version`,
|
|
template.SpeciesID, template.Origin, template.Title, template.Description, template.TriggerType,
|
|
template.MonthFrom, template.DayFrom, template.MonthTo, template.DayTo,
|
|
template.OffsetDaysFrom, template.OffsetDaysTo, template.IntervalDays,
|
|
template.TriggerOffset, template.TriggerOffsetUnit, template.Duration, template.DurationUnit,
|
|
template.Recurrence, template.RecurrenceInterval, template.Priority, template.Active, template.ID, template.Version, gardenID,
|
|
).Scan(&template.UpdatedAt, &template.Version)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return storage.SpeciesTaskTemplate{}, storage.ErrEditConflict
|
|
}
|
|
return storage.SpeciesTaskTemplate{}, err
|
|
}
|
|
return template, nil
|
|
}
|
|
|
|
// Delete removes a task template visible in a garden.
|
|
func (m SpeciesTaskTemplateModel) Delete(gardenID, id int) error {
|
|
return deleteByID(m.DB, `DELETE FROM species_task_templates t USING species s WHERE t.id = $1 AND s.id = t.species_id AND s.garden_id IS NOT DISTINCT FROM NULLIF($2, 0)`, id, gardenID)
|
|
}
|