Skip to content

Commit 02d2aea

Browse files
authored
feat: store and display template creator (#2228)
* design commit * add owner_id to templates table * add owner information in apis and ui * update minWidth for statItem * rename owner to created_by * missing refactor to created_by * handle errors in fetching created_by names
1 parent 46da59a commit 02d2aea

File tree

17 files changed

+135
-24
lines changed

17 files changed

+135
-24
lines changed

coderd/audit/diff_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ func TestDiff(t *testing.T) {
8888
ActiveVersionID: uuid.UUID{3},
8989
MaxTtl: int64(time.Hour),
9090
MinAutostartInterval: int64(time.Minute),
91+
CreatedBy: uuid.NullUUID{UUID: uuid.UUID{4}, Valid: true},
9192
},
9293
exp: audit.Map{
9394
"id": uuid.UUID{1}.String(),
@@ -97,6 +98,7 @@ func TestDiff(t *testing.T) {
9798
"active_version_id": uuid.UUID{3}.String(),
9899
"max_ttl": int64(3600000000000),
99100
"min_autostart_interval": int64(60000000000),
101+
"created_by": uuid.UUID{4}.String(),
100102
},
101103
},
102104
})

coderd/audit/table.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ var AuditableResources = auditMap(map[any]map[string]Action{
7272
"description": ActionTrack,
7373
"max_ttl": ActionTrack,
7474
"min_autostart_interval": ActionTrack,
75+
"created_by": ActionTrack,
7576
},
7677
&database.TemplateVersion{}: {
7778
"id": ActionTrack,

coderd/database/databasefake/databasefake.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1341,6 +1341,7 @@ func (q *fakeQuerier) InsertTemplate(_ context.Context, arg database.InsertTempl
13411341
Description: arg.Description,
13421342
MaxTtl: arg.MaxTtl,
13431343
MinAutostartInterval: arg.MinAutostartInterval,
1344+
CreatedBy: arg.CreatedBy,
13441345
}
13451346
q.templates = append(q.templates, template)
13461347
return template, nil

coderd/database/dump.sql

Lines changed: 5 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ALTER TABLE ONLY templates DROP COLUMN IF EXISTS created_by;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ALTER TABLE ONLY templates ADD COLUMN IF NOT EXISTS created_by uuid REFERENCES users (id) ON DELETE RESTRICT;

coderd/database/models.go

Lines changed: 1 addition & 0 deletions
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: 15 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries/templates.sql

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,11 @@ INSERT INTO
4949
active_version_id,
5050
description,
5151
max_ttl,
52-
min_autostart_interval
52+
min_autostart_interval,
53+
created_by
5354
)
5455
VALUES
55-
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING *;
56+
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING *;
5657

5758
-- name: UpdateTemplateActiveVersionByID :exec
5859
UPDATE

coderd/templates.go

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package coderd
22

33
import (
4+
"context"
45
"database/sql"
56
"errors"
67
"fmt"
@@ -49,7 +50,16 @@ func (api *API) template(rw http.ResponseWriter, r *http.Request) {
4950
count = uint32(workspaceCounts[0].Count)
5051
}
5152

52-
httpapi.Write(rw, http.StatusOK, convertTemplate(template, count))
53+
createdByNameMap, err := getCreatedByNamesByTemplateIDs(r.Context(), api.Database, []database.Template{template})
54+
if err != nil {
55+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
56+
Message: "Internal error fetching creator name.",
57+
Detail: err.Error(),
58+
})
59+
return
60+
}
61+
62+
httpapi.Write(rw, http.StatusOK, convertTemplate(template, count, createdByNameMap[template.ID.String()]))
5363
}
5464

5565
func (api *API) deleteTemplate(rw http.ResponseWriter, r *http.Request) {
@@ -97,6 +107,7 @@ func (api *API) deleteTemplate(rw http.ResponseWriter, r *http.Request) {
97107
func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Request) {
98108
var createTemplate codersdk.CreateTemplateRequest
99109
organization := httpmw.OrganizationParam(r)
110+
apiKey := httpmw.APIKey(r)
100111
if !api.Authorize(rw, r, rbac.ActionCreate, rbac.ResourceTemplate.InOrg(organization.ID)) {
101112
return
102113
}
@@ -175,6 +186,10 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
175186
Description: createTemplate.Description,
176187
MaxTtl: int64(maxTTL),
177188
MinAutostartInterval: int64(minAutostartInterval),
189+
CreatedBy: uuid.NullUUID{
190+
UUID: apiKey.UserID,
191+
Valid: true,
192+
},
178193
})
179194
if err != nil {
180195
return xerrors.Errorf("insert template: %s", err)
@@ -208,7 +223,12 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
208223
}
209224
}
210225

211-
template = convertTemplate(dbTemplate, 0)
226+
createdByNameMap, err := getCreatedByNamesByTemplateIDs(r.Context(), db, []database.Template{dbTemplate})
227+
if err != nil {
228+
return xerrors.Errorf("get creator name: %w", err)
229+
}
230+
231+
template = convertTemplate(dbTemplate, 0, createdByNameMap[dbTemplate.ID.String()])
212232
return nil
213233
})
214234
if err != nil {
@@ -258,7 +278,16 @@ func (api *API) templatesByOrganization(rw http.ResponseWriter, r *http.Request)
258278
return
259279
}
260280

261-
httpapi.Write(rw, http.StatusOK, convertTemplates(templates, workspaceCounts))
281+
createdByNameMap, err := getCreatedByNamesByTemplateIDs(r.Context(), api.Database, templates)
282+
if err != nil {
283+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
284+
Message: "Internal error fetching creator names.",
285+
Detail: err.Error(),
286+
})
287+
return
288+
}
289+
290+
httpapi.Write(rw, http.StatusOK, convertTemplates(templates, workspaceCounts, createdByNameMap))
262291
}
263292

264293
func (api *API) templateByOrganizationAndName(rw http.ResponseWriter, r *http.Request) {
@@ -304,7 +333,16 @@ func (api *API) templateByOrganizationAndName(rw http.ResponseWriter, r *http.Re
304333
count = uint32(workspaceCounts[0].Count)
305334
}
306335

307-
httpapi.Write(rw, http.StatusOK, convertTemplate(template, count))
336+
createdByNameMap, err := getCreatedByNamesByTemplateIDs(r.Context(), api.Database, []database.Template{template})
337+
if err != nil {
338+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
339+
Message: "Internal error fetching creator name.",
340+
Detail: err.Error(),
341+
})
342+
return
343+
}
344+
345+
httpapi.Write(rw, http.StatusOK, convertTemplate(template, count, createdByNameMap[template.ID.String()]))
308346
}
309347

310348
func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
@@ -400,29 +438,54 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
400438
return
401439
}
402440

403-
httpapi.Write(rw, http.StatusOK, convertTemplate(updated, count))
441+
createdByNameMap, err := getCreatedByNamesByTemplateIDs(r.Context(), api.Database, []database.Template{updated})
442+
if err != nil {
443+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
444+
Message: "Internal error fetching creator name.",
445+
Detail: err.Error(),
446+
})
447+
return
448+
}
449+
450+
httpapi.Write(rw, http.StatusOK, convertTemplate(updated, count, createdByNameMap[updated.ID.String()]))
451+
}
452+
453+
func getCreatedByNamesByTemplateIDs(ctx context.Context, db database.Store, templates []database.Template) (map[string]string, error) {
454+
creators := make(map[string]string, len(templates))
455+
for _, template := range templates {
456+
if template.CreatedBy.Valid {
457+
creator, err := db.GetUserByID(ctx, template.CreatedBy.UUID)
458+
if err != nil {
459+
return map[string]string{}, err
460+
}
461+
creators[template.ID.String()] = creator.Username
462+
} else {
463+
creators[template.ID.String()] = ""
464+
}
465+
}
466+
return creators, nil
404467
}
405468

406-
func convertTemplates(templates []database.Template, workspaceCounts []database.GetWorkspaceOwnerCountsByTemplateIDsRow) []codersdk.Template {
469+
func convertTemplates(templates []database.Template, workspaceCounts []database.GetWorkspaceOwnerCountsByTemplateIDsRow, createdByNameMap map[string]string) []codersdk.Template {
407470
apiTemplates := make([]codersdk.Template, 0, len(templates))
408471
for _, template := range templates {
409472
found := false
410473
for _, workspaceCount := range workspaceCounts {
411474
if workspaceCount.TemplateID.String() != template.ID.String() {
412475
continue
413476
}
414-
apiTemplates = append(apiTemplates, convertTemplate(template, uint32(workspaceCount.Count)))
477+
apiTemplates = append(apiTemplates, convertTemplate(template, uint32(workspaceCount.Count), createdByNameMap[template.ID.String()]))
415478
found = true
416479
break
417480
}
418481
if !found {
419-
apiTemplates = append(apiTemplates, convertTemplate(template, uint32(0)))
482+
apiTemplates = append(apiTemplates, convertTemplate(template, uint32(0), createdByNameMap[template.ID.String()]))
420483
}
421484
}
422485
return apiTemplates
423486
}
424487

425-
func convertTemplate(template database.Template, workspaceOwnerCount uint32) codersdk.Template {
488+
func convertTemplate(template database.Template, workspaceOwnerCount uint32, createdByName string) codersdk.Template {
426489
return codersdk.Template{
427490
ID: template.ID,
428491
CreatedAt: template.CreatedAt,
@@ -435,5 +498,7 @@ func convertTemplate(template database.Template, workspaceOwnerCount uint32) cod
435498
Description: template.Description,
436499
MaxTTLMillis: time.Duration(template.MaxTtl).Milliseconds(),
437500
MinAutostartIntervalMillis: time.Duration(template.MinAutostartInterval).Milliseconds(),
501+
CreatedByID: template.CreatedBy,
502+
CreatedByName: createdByName,
438503
}
439504
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy