internal/store/teams.go
151 lines · 3777 bytes
1package store
2
3import (
4 "database/sql"
5 "errors"
6 "fmt"
7)
8
9type Team struct {
10 ID int64
11 OrgID int64
12 Name string
13}
14
15func (s *Store) CreateTeam(orgID int64, name string) (int64, error) {
16 res, err := s.DB.Exec("INSERT INTO teams (org_id, name) VALUES (?, ?)", orgID, name)
17 if err != nil {
18 if isUniqueErr(err) {
19 return 0, fmt.Errorf("team %q already exists", name)
20 }
21 return 0, err
22 }
23 return res.LastInsertId()
24}
25
26func (s *Store) TeamByName(orgID int64, name string) (Team, error) {
27 var t Team
28 err := s.DB.QueryRow("SELECT id, org_id, name FROM teams WHERE org_id = ? AND name = ?",
29 orgID, name).Scan(&t.ID, &t.OrgID, &t.Name)
30 if errors.Is(err, sql.ErrNoRows) {
31 return t, ErrNotFound
32 }
33 return t, err
34}
35
36func (s *Store) DeleteTeam(teamID int64) error {
37 res, err := s.DB.Exec("DELETE FROM teams WHERE id = ?", teamID)
38 if err != nil {
39 return err
40 }
41 if n, _ := res.RowsAffected(); n == 0 {
42 return ErrNotFound
43 }
44 return nil
45}
46
47func (s *Store) ListTeams(orgID int64) ([]Team, error) {
48 rows, err := s.DB.Query("SELECT id, org_id, name FROM teams WHERE org_id = ? ORDER BY name", orgID)
49 if err != nil {
50 return nil, err
51 }
52 defer rows.Close()
53 var out []Team
54 for rows.Next() {
55 var t Team
56 if err := rows.Scan(&t.ID, &t.OrgID, &t.Name); err != nil {
57 return nil, err
58 }
59 out = append(out, t)
60 }
61 return out, rows.Err()
62}
63
64func (s *Store) AddTeamMember(teamID, userID int64) error {
65 _, err := s.DB.Exec(
66 "INSERT INTO team_members (team_id, user_id) VALUES (?, ?) ON CONFLICT DO NOTHING",
67 teamID, userID)
68 return err
69}
70
71func (s *Store) RemoveTeamMember(teamID, userID int64) error {
72 res, err := s.DB.Exec("DELETE FROM team_members WHERE team_id = ? AND user_id = ?", teamID, userID)
73 if err != nil {
74 return err
75 }
76 if n, _ := res.RowsAffected(); n == 0 {
77 return ErrNotFound
78 }
79 return nil
80}
81
82func (s *Store) TeamMembers(teamID int64) ([]string, error) {
83 rows, err := s.DB.Query(`
84 SELECT u.username FROM team_members tm JOIN users u ON u.id = tm.user_id
85 WHERE tm.team_id = ? ORDER BY u.username`, teamID)
86 if err != nil {
87 return nil, err
88 }
89 defer rows.Close()
90 var out []string
91 for rows.Next() {
92 var n string
93 if err := rows.Scan(&n); err != nil {
94 return nil, err
95 }
96 out = append(out, n)
97 }
98 return out, rows.Err()
99}
100
101// GrantTeamRepo attaches (or updates) a team's role on a repo.
102func (s *Store) GrantTeamRepo(teamID, repoID int64, role string) error {
103 _, err := s.DB.Exec(`
104 INSERT INTO team_repos (team_id, repo_id, role) VALUES (?, ?, ?)
105 ON CONFLICT (team_id, repo_id) DO UPDATE SET role = excluded.role`,
106 teamID, repoID, role)
107 return err
108}
109
110func (s *Store) RevokeTeamRepo(teamID, repoID int64) error {
111 res, err := s.DB.Exec("DELETE FROM team_repos WHERE team_id = ? AND repo_id = ?", teamID, repoID)
112 if err != nil {
113 return err
114 }
115 if n, _ := res.RowsAffected(); n == 0 {
116 return ErrNotFound
117 }
118 return nil
119}
120
121type TeamGrant struct {
122 RepoPath string `json:"repo"`
123 Role string `json:"role"`
124}
125
126func (s *Store) TeamGrants(teamID int64) ([]TeamGrant, error) {
127 rows, err := s.DB.Query(`
128 SELECT COALESCE(u.username, o.name) || '/' || r.name, tr.role
129 FROM team_repos tr JOIN repos r ON r.id = tr.repo_id
130 LEFT JOIN users u ON r.owner_kind = 'user' AND u.id = r.owner_id
131 LEFT JOIN orgs o ON r.owner_kind = 'org' AND o.id = r.owner_id
132 WHERE tr.team_id = ? ORDER BY 1`, teamID)
133 if err != nil {
134 return nil, err
135 }
136 defer rows.Close()
137 var out []TeamGrant
138 for rows.Next() {
139 var g TeamGrant
140 if err := rows.Scan(&g.RepoPath, &g.Role); err != nil {
141 return nil, err
142 }
143 out = append(out, g)
144 }
145 return out, rows.Err()
146}
147
148func (s *Store) SetOrgMembersRole(orgID int64, role string) error {
149 _, err := s.DB.Exec("UPDATE orgs SET members_role = ? WHERE id = ?", role, orgID)
150 return err
151}