1package database
2
3import (
4 "context"
5
6 "github.com/charmbracelet/soft-serve/pkg/access"
7 "github.com/charmbracelet/soft-serve/pkg/db"
8 "github.com/charmbracelet/soft-serve/pkg/db/models"
9 "github.com/charmbracelet/soft-serve/pkg/store"
10 "github.com/charmbracelet/soft-serve/pkg/utils"
11)
12
13var _ store.OrgStore = (*orgStore)(nil)
14
15type orgStore struct{ *handleStore }
16
17// UpdateOrgContactEmail implements store.OrgStore.
18func (*orgStore) UpdateOrgContactEmail(ctx context.Context, h db.Handler, org int64, email string) error {
19 if err := utils.ValidateEmail(email); err != nil {
20 return err
21 }
22
23 query := h.Rebind(`
24 UPDATE organizations
25 SET
26 contact_email = ?
27 WHERE
28 id = ?
29 `)
30
31 _, err := h.ExecContext(ctx, query, email, org)
32 return err
33}
34
35// ListOrgs implements store.OrgStore.
36func (*orgStore) ListOrgs(ctx context.Context, h db.Handler, uid int64) ([]models.Organization, error) {
37 var m []models.Organization
38 query := h.Rebind(`
39 SELECT
40 o.*,
41 h AS handle
42 FROM
43 organizations o
44 JOIN handles h ON h.id = o.handle_id
45 JOIN organization_members om ON om.org_id = o.id
46 WHERE
47 o.user_id = ?
48 `)
49 err := h.SelectContext(ctx, &m, query, uid)
50 return m, err
51}
52
53// Delete implements store.OrgStore.
54func (s *orgStore) DeleteOrgByID(ctx context.Context, h db.Handler, user, id int64) error {
55 _, err := s.getOrgByIDWithAccess(ctx, h, user, id, access.AdminAccess)
56 if err != nil {
57 return err
58 }
59 query := h.Rebind(`DELETE FROM organizations WHERE id = ?;`)
60 _, err = h.ExecContext(ctx, query, id)
61 return err
62}
63
64// Create implements store.OrgStore.
65func (s *orgStore) CreateOrg(ctx context.Context, h db.Handler, user int64, name, email string) (models.Organization, error) {
66 if err := utils.ValidateEmail(email); err != nil {
67 return models.Organization{}, err
68 }
69
70 handle, err := s.CreateHandle(ctx, h, name)
71 if err != nil {
72 return models.Organization{}, err
73 }
74
75 query := h.Rebind(`
76 INSERT INTO
77 organizations (handle_id, contact_email, updated_at)
78 VALUES
79 (?, ?, CURRENT_TIMESTAMP) RETURNING id;
80 `)
81
82 var id int64
83 if err := h.GetContext(ctx, &id, query, handle, email); err != nil {
84 return models.Organization{}, err
85 }
86 if err := s.AddUserToOrg(ctx, h, id, user, access.AdminAccess); err != nil {
87 return models.Organization{}, err
88 }
89
90 return s.GetOrgByID(ctx, h, user, id)
91}
92
93func (*orgStore) UpdateUserAccessInOrg(ctx context.Context, h db.Handler, org, user int64, lvl access.AccessLevel) error {
94 query := h.Rebind(`
95 UPDATE organization_members
96 WHERE
97 organization_id = ?
98 AND user_id = ?
99 SET
100 access_level = ?
101 `)
102 _, err := h.ExecContext(ctx, query, org, user, lvl)
103 return err
104}
105
106func (*orgStore) RemoveUserFromOrg(ctx context.Context, h db.Handler, org, user int64) error {
107 query := h.Rebind(`
108 DELETE FROM organization_members
109 WHERE
110 organization_id = ?
111 AND user_id = ?
112 `)
113 _, err := h.ExecContext(ctx, query, org, user)
114 return err
115}
116
117func (*orgStore) AddUserToOrg(ctx context.Context, h db.Handler, org, user int64, lvl access.AccessLevel) error {
118 query := h.Rebind(`
119 INSERT INTO
120 organization_members (
121 organization_id,
122 user_id,
123 access_level,
124 updated_at
125 )
126 VALUES
127 (?, ?, ?, CURRENT_TIMESTAMP);
128 `)
129 _, err := h.ExecContext(ctx, query, org, user, lvl)
130 return err
131}
132
133// FindByName implements store.OrgStore.
134func (*orgStore) FindOrgByHandle(ctx context.Context, h db.Handler, user int64, name string) (models.Organization, error) {
135 var m models.Organization
136 query := h.Rebind(`
137 SELECT
138 o.*,
139 h AS handle
140 FROM
141 organizations o
142 JOIN handles h ON h.id = o.handle_id
143 JOIN organization_members om ON om.organization_id = o.id
144 WHERE
145 om.user_id = ?
146 AND h.handle = ?;
147 `)
148 err := h.GetContext(ctx, &m, query, user, name)
149 return m, err
150}
151
152// GetByID implements store.OrgStore.
153func (s *orgStore) GetOrgByID(ctx context.Context, h db.Handler, user, id int64) (models.Organization, error) {
154 return s.getOrgByIDWithAccess(ctx, h, user, id, access.ReadOnlyAccess)
155}
156
157func (*orgStore) getOrgByIDWithAccess(ctx context.Context, h db.Handler, user, id int64, level access.AccessLevel) (models.Organization, error) {
158 var m models.Organization
159 query := h.Rebind(`
160 SELECT
161 o.*,
162 h AS handle
163 FROM
164 organizations o
165 JOIN handles h ON h.id = o.handle_id
166 JOIN organization_members om ON om.organization_id = o.id
167 WHERE
168 om.user_id = ?
169 AND id = ?
170 AND om.access_level >= ?;
171 `)
172 err := h.GetContext(ctx, &m, query, user, id, level)
173 return m, err
174}