// savedview.go stores named tracker reports. A saved view is a NAME for
// a URL this forge could already produce — it invents no query the
// address bar cannot say, which is why it needs no schema of its own
// beyond the name and the parameters, and why deleting one loses
// nothing but the shortcut.
package oltp
const savedViewSchema = `
CREATE TABLE IF NOT EXISTS saved_view (
account TEXT NOT NULL,
-- The listing this view belongs to: a repository's tracker name, or
-- the empty string for the cross-repository listing. A report saved
-- against one tracker means nothing on another, so the two never
-- appear in each other's rail.
repo TEXT NOT NULL,
name TEXT NOT NULL,
query TEXT NOT NULL, -- the report, as a URL query string
at TEXT NOT NULL,
PRIMARY KEY (account, repo, name)
);`
// SavedView is one named report.
type SavedView struct {
Name string `json:"name"`
Query string `json:"query"`
}
// SaveView records a report under a name, replacing a view of the same
// name: saving twice with one name is how a reader corrects it, not an
// error and not a second entry that shadows the first.
func (d *DB) SaveView(account, repo, name, query string) error {
_, err := d.w.Exec(`INSERT INTO saved_view (account, repo, name, query, at)
VALUES (?, ?, ?, ?, ?)
ON CONFLICT (account, repo, name)
DO UPDATE SET query = excluded.query, at = excluded.at`,
account, repo, name, query, now())
return err
}
// DeleteView drops one. A view nobody has is not an error to remove:
// the caller asked for it to be gone and afterwards it is.
func (d *DB) DeleteView(account, repo, name string) error {
_, err := d.w.Exec(`DELETE FROM saved_view
WHERE account = ? AND repo = ? AND name = ?`, account, repo, name)
return err
}
// SavedViews lists one account's views for one listing, by name — the
// order a reader put them in their own head, not by when they saved.
func (d *DB) SavedViews(account, repo string) ([]SavedView, error) {
if account == "" {
return nil, nil
}
rows, err := d.db.Query(`SELECT name, query FROM saved_view
WHERE account = ? AND repo = ? ORDER BY lower(name)`, account, repo)
if err != nil {
return nil, err
}
defer rows.Close()
var out []SavedView
for rows.Next() {
var v SavedView
if err := rows.Scan(&v.Name, &v.Query); err != nil {
return nil, err
}
out = append(out, v)
}
return out, rows.Err()
}