internal/store/dashboard.go

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

120 lines · 3900 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	           JOIN orgs oo ON oo.id = mm.org_id
 22	           WHERE r.owner_kind = 'org' AND mm.org_id = r.owner_id AND mm.user_id = ?1
 23	             AND (mm.role = 'admin' OR oo.members_role <> 'none'))
 24	OR EXISTS (SELECT 1 FROM team_repos tr
 25	           JOIN team_members tm ON tm.team_id = tr.team_id AND tm.user_id = ?1
 26	           WHERE tr.repo_id = r.id)
 27)`
 28
 29func (s *Store) dashboardQuery(q string, userID int64) ([]DashboardItem, error) {
 30	rows, err := s.DB.Query(q, userID)
 31	if err != nil {
 32		return nil, err
 33	}
 34	defer rows.Close()
 35	var out []DashboardItem
 36	for rows.Next() {
 37		var d DashboardItem
 38		if err := rows.Scan(&d.RepoPath, &d.Number, &d.Title, &d.Author, &d.State, &d.UpdatedAt); err != nil {
 39			return nil, err
 40		}
 41		out = append(out, d)
 42	}
 43	return out, rows.Err()
 44}
 45
 46// DashboardMRs returns open merge requests involving the user: on their
 47// repositories (owned, granted, org) or authored by them anywhere.
 48func (s *Store) DashboardMRs(userID int64) ([]DashboardItem, error) {
 49	return s.dashboardQuery(`
 50		SELECT COALESCE(u.username, o.name) || '/' || r.name,
 51		       x.number, x.title, au.username, x.state, x.updated_at
 52		FROM merge_requests x
 53		JOIN repos r ON r.id = x.repo_id
 54		LEFT JOIN users u ON r.owner_kind = 'user' AND u.id = r.owner_id
 55		LEFT JOIN orgs o  ON r.owner_kind = 'org'  AND o.id = r.owner_id
 56		JOIN users au ON au.id = x.author_id
 57		WHERE x.state IN ('open', 'source_gone') AND `+involvedCond+`
 58		ORDER BY x.updated_at DESC LIMIT 50`, userID)
 59}
 60
 61// DashboardIssues is the issue counterpart of DashboardMRs.
 62func (s *Store) DashboardIssues(userID int64) ([]DashboardItem, error) {
 63	return s.dashboardQuery(`
 64		SELECT COALESCE(u.username, o.name) || '/' || r.name,
 65		       x.number, x.title, au.username, x.state, x.updated_at
 66		FROM issues x
 67		JOIN repos r ON r.id = x.repo_id
 68		LEFT JOIN users u ON r.owner_kind = 'user' AND u.id = r.owner_id
 69		LEFT JOIN orgs o  ON r.owner_kind = 'org'  AND o.id = r.owner_id
 70		JOIN users au ON au.id = x.author_id
 71		WHERE x.state = 'open' AND `+involvedCond+`
 72		ORDER BY x.updated_at DESC LIMIT 50`, userID)
 73}
 74
 75func (s *Store) PinRepo(userID, repoID int64) error {
 76	_, err := s.DB.Exec(
 77		"INSERT INTO repo_pins (user_id, repo_id) VALUES (?, ?) ON CONFLICT DO NOTHING",
 78		userID, repoID)
 79	return err
 80}
 81
 82func (s *Store) IsPinned(userID, repoID int64) bool {
 83	var n int
 84	s.DB.QueryRow("SELECT COUNT(*) FROM repo_pins WHERE user_id = ? AND repo_id = ?",
 85		userID, repoID).Scan(&n)
 86	return n > 0
 87}
 88
 89func (s *Store) UnpinRepo(userID, repoID int64) error {
 90	res, err := s.DB.Exec(
 91		"DELETE FROM repo_pins WHERE user_id = ? AND repo_id = ?", userID, repoID)
 92	if err != nil {
 93		return err
 94	}
 95	if n, _ := res.RowsAffected(); n == 0 {
 96		return ErrNotFound
 97	}
 98	return nil
 99}
100
101// PinnedRepos returns the user's pinned repositories in pin order. The
102// caller applies visibility checks before rendering.
103func (s *Store) PinnedRepos(userID int64) ([]Repo, error) {
104	rows, err := s.DB.Query(repoSelect+`
105		JOIN repo_pins p ON p.repo_id = r.id AND p.user_id = ?
106		ORDER BY p.pinned_at`, userID)
107	if err != nil {
108		return nil, err
109	}
110	defer rows.Close()
111	var out []Repo
112	for rows.Next() {
113		r, err := scanRepo(rows)
114		if err != nil {
115			return nil, err
116		}
117		out = append(out, r)
118	}
119	return out, rows.Err()
120}