internal/store/dashboard.go

c327fbdf04d6735772a5d661b271d68fe9e39404
gitbay/internal/store/dashboard.go history · blame · raw

108 lines · 3438 bytes

  1package store
  2
  3// DashboardItem is one open issue or MR row on the logged-in homepage.
  4type DashboardItem struct {
  5	RepoPath  string
  6	Number    int64
  7	Title     string
  8	Author    string
  9	State     string
 10	UpdatedAt string
 11}
 12
 13// involvedRepos filters to repositories the user owns, is granted on, or
 14// reaches through org membership — or rows the user authored anywhere.
 15const involvedCond = `(
 16	x.author_id = ?1
 17	OR (r.owner_kind = 'user' AND r.owner_id = ?1)
 18	OR EXISTS (SELECT 1 FROM repo_access a
 19	           WHERE a.repo_id = r.id AND a.subject_kind = 'user' AND a.subject_id = ?1)
 20	OR EXISTS (SELECT 1 FROM org_members mm
 21	           WHERE r.owner_kind = 'org' AND mm.org_id = r.owner_id AND mm.user_id = ?1)
 22)`
 23
 24func (s *Store) dashboardQuery(q string, userID int64) ([]DashboardItem, error) {
 25	rows, err := s.DB.Query(q, userID)
 26	if err != nil {
 27		return nil, err
 28	}
 29	defer rows.Close()
 30	var out []DashboardItem
 31	for rows.Next() {
 32		var d DashboardItem
 33		if err := rows.Scan(&d.RepoPath, &d.Number, &d.Title, &d.Author, &d.State, &d.UpdatedAt); err != nil {
 34			return nil, err
 35		}
 36		out = append(out, d)
 37	}
 38	return out, rows.Err()
 39}
 40
 41// DashboardMRs returns open merge requests involving the user: on their
 42// repositories (owned, granted, org) or authored by them anywhere.
 43func (s *Store) DashboardMRs(userID int64) ([]DashboardItem, error) {
 44	return s.dashboardQuery(`
 45		SELECT COALESCE(u.username, o.name) || '/' || r.name,
 46		       x.number, x.title, au.username, x.state, x.updated_at
 47		FROM merge_requests x
 48		JOIN repos r ON r.id = x.repo_id
 49		LEFT JOIN users u ON r.owner_kind = 'user' AND u.id = r.owner_id
 50		LEFT JOIN orgs o  ON r.owner_kind = 'org'  AND o.id = r.owner_id
 51		JOIN users au ON au.id = x.author_id
 52		WHERE x.state IN ('open', 'source_gone') AND `+involvedCond+`
 53		ORDER BY x.updated_at DESC LIMIT 50`, userID)
 54}
 55
 56// DashboardIssues is the issue counterpart of DashboardMRs.
 57func (s *Store) DashboardIssues(userID int64) ([]DashboardItem, error) {
 58	return s.dashboardQuery(`
 59		SELECT COALESCE(u.username, o.name) || '/' || r.name,
 60		       x.number, x.title, au.username, x.state, x.updated_at
 61		FROM issues x
 62		JOIN repos r ON r.id = x.repo_id
 63		LEFT JOIN users u ON r.owner_kind = 'user' AND u.id = r.owner_id
 64		LEFT JOIN orgs o  ON r.owner_kind = 'org'  AND o.id = r.owner_id
 65		JOIN users au ON au.id = x.author_id
 66		WHERE x.state = 'open' AND `+involvedCond+`
 67		ORDER BY x.updated_at DESC LIMIT 50`, userID)
 68}
 69
 70func (s *Store) PinRepo(userID, repoID int64) error {
 71	_, err := s.DB.Exec(
 72		"INSERT INTO repo_pins (user_id, repo_id) VALUES (?, ?) ON CONFLICT DO NOTHING",
 73		userID, repoID)
 74	return err
 75}
 76
 77func (s *Store) UnpinRepo(userID, repoID int64) error {
 78	res, err := s.DB.Exec(
 79		"DELETE FROM repo_pins WHERE user_id = ? AND repo_id = ?", userID, repoID)
 80	if err != nil {
 81		return err
 82	}
 83	if n, _ := res.RowsAffected(); n == 0 {
 84		return ErrNotFound
 85	}
 86	return nil
 87}
 88
 89// PinnedRepos returns the user's pinned repositories in pin order. The
 90// caller applies visibility checks before rendering.
 91func (s *Store) PinnedRepos(userID int64) ([]Repo, error) {
 92	rows, err := s.DB.Query(repoSelect+`
 93		JOIN repo_pins p ON p.repo_id = r.id AND p.user_id = ?
 94		ORDER BY p.pinned_at`, userID)
 95	if err != nil {
 96		return nil, err
 97	}
 98	defer rows.Close()
 99	var out []Repo
100	for rows.Next() {
101		r, err := scanRepo(rows)
102		if err != nil {
103			return nil, err
104		}
105		out = append(out, r)
106	}
107	return out, rows.Err()
108}