Skip to content

Commit 6883106

Browse files
committed
fix sqlx woes
1 parent 0218c4e commit 6883106

File tree

10 files changed

+226
-25
lines changed

10 files changed

+226
-25
lines changed

coderd/coderd.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ func New(options *Options) *API {
319319
r.Get("/", api.template)
320320
r.Delete("/", api.deleteTemplate)
321321
r.Patch("/", api.patchTemplateMeta)
322+
r.Get("/user-roles", api.templateUserRoles)
322323
r.Route("/versions", func(r chi.Router) {
323324
r.Get("/", api.templateVersionsByTemplate)
324325
r.Patch("/", api.patchActiveTemplateVersion)

coderd/database/databasefake/databasefake.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/lib/pq"
1313
"golang.org/x/exp/maps"
1414
"golang.org/x/exp/slices"
15+
"golang.org/x/xerrors"
1516

1617
"github.com/coder/coder/coderd/database"
1718
"github.com/coder/coder/coderd/rbac"
@@ -1244,6 +1245,45 @@ func (q *fakeQuerier) UpdateTemplateUserACLByID(_ context.Context, id uuid.UUID,
12441245
return sql.ErrNoRows
12451246
}
12461247

1248+
func (q *fakeQuerier) GetTemplateUserRoles(ctx context.Context, id uuid.UUID) ([]database.TemplateUser, error) {
1249+
q.mutex.RLock()
1250+
defer q.mutex.RUnlock()
1251+
1252+
var template database.Template
1253+
for _, t := range q.templates {
1254+
if t.ID == id {
1255+
template = t
1256+
break
1257+
}
1258+
}
1259+
1260+
if template.ID == uuid.Nil {
1261+
return nil, sql.ErrNoRows
1262+
}
1263+
1264+
acl := template.UserACL()
1265+
1266+
users := make([]database.TemplateUser, 0, len(acl))
1267+
for k, v := range acl {
1268+
user, err := q.GetUserByID(context.Background(), uuid.MustParse(k))
1269+
if err != nil && xerrors.Is(err, sql.ErrNoRows) {
1270+
return nil, xerrors.Errorf("get user by ID: %w", err)
1271+
}
1272+
// We don't delete users from the map if they
1273+
// get deleted so just skip.
1274+
if xerrors.Is(err, sql.ErrNoRows) {
1275+
continue
1276+
}
1277+
1278+
users = append(users, database.TemplateUser{
1279+
User: user,
1280+
Role: v,
1281+
})
1282+
}
1283+
1284+
return users, nil
1285+
}
1286+
12471287
func (q *fakeQuerier) GetOrganizationMemberByUserID(_ context.Context, arg database.GetOrganizationMemberByUserIDParams) (database.OrganizationMember, error) {
12481288
q.mutex.RLock()
12491289
defer q.mutex.RUnlock()

coderd/database/db.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,16 @@ type DBTX interface {
3131
PrepareContext(context.Context, string) (*sql.Stmt, error)
3232
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
3333
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
34+
SelectContext(ctx context.Context, dest interface{}, query string, args ...interface{}) error
35+
GetContext(ctx context.Context, dest interface{}, query string, args ...interface{}) error
3436
}
3537

3638
// New creates a new database store using a SQL database connection.
3739
func New(sdb *sql.DB) Store {
40+
dbx := sqlx.NewDb(sdb, "postgres")
3841
return &sqlQuerier{
39-
db: sdb,
40-
sdb: sqlx.NewDb(sdb, "postgres"),
42+
db: dbx,
43+
sdb: dbx,
4144
}
4245
}
4346

@@ -66,7 +69,7 @@ func (q *sqlQuerier) InTx(function func(Store) error) error {
6669
return nil
6770
}
6871

69-
transaction, err := q.sdb.Begin()
72+
transaction, err := q.sdb.BeginTxx(context.Background(), nil)
7073
if err != nil {
7174
return xerrors.Errorf("begin transaction: %w", err)
7275
}

coderd/database/modelqueries.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ type customQuerier interface {
1717

1818
type templateQuerier interface {
1919
UpdateTemplateUserACLByID(ctx context.Context, id uuid.UUID, acl UserACL) error
20+
GetTemplateUserRoles(ctx context.Context, id uuid.UUID) ([]TemplateUser, error)
2021
}
2122

2223
type TemplateUser struct {
@@ -45,3 +46,38 @@ WHERE
4546

4647
return nil
4748
}
49+
50+
func (q *sqlQuerier) GetTemplateUserRoles(ctx context.Context, id uuid.UUID) ([]TemplateUser, error) {
51+
const query = `
52+
SELECT
53+
perms.value as role, users.*
54+
FROM
55+
users
56+
JOIN
57+
(
58+
SELECT
59+
*
60+
FROM
61+
jsonb_each_text(
62+
(
63+
SELECT
64+
templates.user_acl
65+
FROM
66+
templates
67+
WHERE
68+
id = $1
69+
)
70+
)
71+
) AS perms
72+
ON
73+
users.id::text = perms.key;
74+
`
75+
76+
var tus []TemplateUser
77+
err := q.db.SelectContext(ctx, &tus, query, id.String())
78+
if err != nil {
79+
return nil, xerrors.Errorf("select context: %w", err)
80+
}
81+
82+
return tus, nil
83+
}

coderd/database/models.go

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries.sql.go

Lines changed: 17 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/sqlc.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ packages:
1616
# deleted after generation.
1717
output_db_file_name: db_tmp.go
1818

19+
overrides:
20+
- column: "users.rbac_roles"
21+
go_type: "github.com/lib/pq.StringArray"
22+
1923
rename:
2024
api_key: APIKey
2125
api_key_scope: APIKeyScope

coderd/templates.go

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,47 @@ func (api *API) templateDAUs(rw http.ResponseWriter, r *http.Request) {
625625
httpapi.Write(rw, http.StatusOK, resp)
626626
}
627627

628+
func (api *API) templateUserRoles(rw http.ResponseWriter, r *http.Request) {
629+
template := httpmw.TemplateParam(r)
630+
if !api.Authorize(r, rbac.ActionRead, template) {
631+
httpapi.ResourceNotFound(rw)
632+
return
633+
}
634+
635+
users, err := api.Database.GetTemplateUserRoles(r.Context(), template.ID)
636+
if err != nil {
637+
httpapi.InternalServerError(rw, err)
638+
return
639+
}
640+
641+
users, err = AuthorizeFilter(api.httpAuth, r, rbac.ActionRead, users)
642+
if err != nil {
643+
httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{
644+
Message: "Internal error fetching users.",
645+
Detail: err.Error(),
646+
})
647+
return
648+
}
649+
650+
userIDs := make([]uuid.UUID, 0, len(users))
651+
for _, user := range users {
652+
userIDs = append(userIDs, user.ID)
653+
}
654+
655+
orgIDsByMemberIDsRows, err := api.Database.GetOrganizationIDsByMemberIDs(r.Context(), userIDs)
656+
if err != nil {
657+
httpapi.InternalServerError(rw, err)
658+
return
659+
}
660+
661+
organizationIDsByUserID := map[uuid.UUID][]uuid.UUID{}
662+
for _, organizationIDsByMemberIDsRow := range orgIDsByMemberIDsRows {
663+
organizationIDsByUserID[organizationIDsByMemberIDsRow.UserID] = organizationIDsByMemberIDsRow.OrganizationIDs
664+
}
665+
666+
httpapi.Write(rw, http.StatusOK, convertTemplateUsers(users, organizationIDsByUserID))
667+
}
668+
628669
type autoImportTemplateOpts struct {
629670
name string
630671
archive []byte
@@ -828,8 +869,8 @@ func (api *API) convertTemplate(
828869
}
829870
}
830871

831-
func convertTemplateACL(acl database.UserACL) codersdk.TemplateUserACL {
832-
userACL := make(codersdk.TemplateUserACL, len(acl))
872+
func convertTemplateACL(acl database.UserACL) map[string]codersdk.TemplateRole {
873+
userACL := make(map[string]codersdk.TemplateRole, len(acl))
833874
for k, v := range acl {
834875
userACL[k] = convertDatabaseTemplateRole(v)
835876
}
@@ -871,3 +912,16 @@ func validateTemplateRole(role codersdk.TemplateRole) error {
871912

872913
return nil
873914
}
915+
916+
func convertTemplateUsers(tus []database.TemplateUser, orgIDsByUserIDs map[uuid.UUID][]uuid.UUID) []codersdk.TemplateUser {
917+
users := make([]codersdk.TemplateUser, 0, len(tus))
918+
919+
for _, tu := range tus {
920+
users = append(users, codersdk.TemplateUser{
921+
User: convertUser(tu.User, orgIDsByUserIDs[tu.User.ID]),
922+
Role: codersdk.TemplateRole(tu.Role),
923+
})
924+
}
925+
926+
return users
927+
}

coderd/templates_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -897,6 +897,52 @@ func TestDeleteTemplate(t *testing.T) {
897897
})
898898
}
899899

900+
func TestTemplateUserRoles(t *testing.T) {
901+
t.Parallel()
902+
903+
t.Run("OK", func(t *testing.T) {
904+
t.Parallel()
905+
client := coderdtest.New(t, nil)
906+
user := coderdtest.CreateFirstUser(t, client)
907+
_, user2 := coderdtest.CreateAnotherUserWithUser(t, client, user.OrganizationID)
908+
_, user3 := coderdtest.CreateAnotherUserWithUser(t, client, user.OrganizationID)
909+
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
910+
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID,
911+
func(r *codersdk.CreateTemplateRequest) {
912+
r.IsPrivate = true
913+
},
914+
)
915+
916+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
917+
defer cancel()
918+
919+
_, err := client.UpdateTemplateMeta(ctx, template.ID, codersdk.UpdateTemplateMeta{
920+
UserPerms: map[string]codersdk.TemplateRole{
921+
user2.ID.String(): codersdk.TemplateRoleRead,
922+
user3.ID.String(): codersdk.TemplateRoleWrite,
923+
},
924+
})
925+
require.NoError(t, err)
926+
927+
users, err := client.TemplateUserRoles(ctx, template.ID)
928+
require.NoError(t, err)
929+
930+
templateUser2 := codersdk.TemplateUser{
931+
User: user2,
932+
Role: codersdk.TemplateRoleRead,
933+
}
934+
935+
templateUser3 := codersdk.TemplateUser{
936+
User: user3,
937+
Role: codersdk.TemplateRoleWrite,
938+
}
939+
940+
require.Len(t, users, 2)
941+
require.Contains(t, users, templateUser2)
942+
require.Contains(t, users, templateUser3)
943+
})
944+
}
945+
900946
func TestTemplateDAUs(t *testing.T) {
901947
t.Parallel()
902948

0 commit comments

Comments
 (0)