Skip to content

Commit a67043e

Browse files
committed
Complete frontend
1 parent 055135a commit a67043e

File tree

15 files changed

+90
-53
lines changed

15 files changed

+90
-53
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ site/out/
3939
*.lock.hcl
4040
.terraform/
4141

42-
.vscode/*
42+
.vscode/*.log
43+
.vscode/launch.json
4344
**/*.swp
4445
.coderv2/*
4546
**/__debug_bin

cli/create.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ func create() *cobra.Command {
7272
}
7373

7474
slices.SortFunc(templates, func(a, b codersdk.Template) bool {
75-
return a.WorkspaceOwnerCount > b.WorkspaceOwnerCount
75+
return a.ActiveUserCount > b.ActiveUserCount
7676
})
7777

7878
templateNames := make([]string, 0, len(templates))
@@ -81,13 +81,13 @@ func create() *cobra.Command {
8181
for _, template := range templates {
8282
templateName := template.Name
8383

84-
if template.WorkspaceOwnerCount > 0 {
84+
if template.ActiveUserCount > 0 {
8585
developerText := "developer"
86-
if template.WorkspaceOwnerCount != 1 {
86+
if template.ActiveUserCount != 1 {
8787
developerText = "developers"
8888
}
8989

90-
templateName += cliui.Styles.Placeholder.Render(fmt.Sprintf(" (used by %d %s)", template.WorkspaceOwnerCount, developerText))
90+
templateName += cliui.Styles.Placeholder.Render(fmt.Sprintf(" (used by %d %s)", template.ActiveUserCount, developerText))
9191
}
9292

9393
templateNames = append(templateNames, templateName)

cli/templates.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func displayTemplates(filterColumns []string, templates ...codersdk.Template) (s
6565
rows := make([]templateTableRow, len(templates))
6666
for i, template := range templates {
6767
suffix := ""
68-
if template.WorkspaceOwnerCount != 1 {
68+
if template.ActiveUserCount != 1 {
6969
suffix = "s"
7070
}
7171

@@ -76,7 +76,7 @@ func displayTemplates(filterColumns []string, templates ...codersdk.Template) (s
7676
OrganizationID: template.OrganizationID,
7777
Provisioner: template.Provisioner,
7878
ActiveVersionID: template.ActiveVersionID,
79-
UsedBy: cliui.Styles.Fuchsia.Render(fmt.Sprintf("%d developer%s", template.WorkspaceOwnerCount, suffix)),
79+
UsedBy: cliui.Styles.Fuchsia.Render(fmt.Sprintf("%d developer%s", template.ActiveUserCount, suffix)),
8080
MaxTTL: (time.Duration(template.MaxTTLMillis) * time.Millisecond),
8181
MinAutostartInterval: (time.Duration(template.MinAutostartIntervalMillis) * time.Millisecond),
8282
}

coderd/metricscache/metricscache.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ type Cache struct {
2929
templateDAUResponses atomic.Pointer[map[uuid.UUID]codersdk.TemplateDAUsResponse]
3030
templateUniqueUsers atomic.Pointer[map[uuid.UUID]int]
3131

32-
doneCh chan struct{}
32+
done chan struct{}
3333
cancel func()
3434

3535
interval time.Duration
@@ -44,7 +44,7 @@ func New(db database.Store, log slog.Logger, interval time.Duration) *Cache {
4444
c := &Cache{
4545
database: db,
4646
log: log,
47-
doneCh: make(chan struct{}),
47+
done: make(chan struct{}),
4848
cancel: cancel,
4949
interval: interval,
5050
}
@@ -146,11 +146,13 @@ func (c *Cache) refresh(ctx context.Context) error {
146146
}
147147

148148
func (c *Cache) run(ctx context.Context) {
149-
defer close(c.doneCh)
149+
defer close(c.done)
150150

151151
ticker := time.NewTicker(c.interval)
152152
defer ticker.Stop()
153153

154+
var loaded bool
155+
154156
for {
155157
for r := retry.New(time.Millisecond*100, time.Minute); r.Wait(ctx); {
156158
start := time.Now()
@@ -173,7 +175,7 @@ func (c *Cache) run(ctx context.Context) {
173175

174176
select {
175177
case <-ticker.C:
176-
case <-c.doneCh:
178+
case <-c.done:
177179
return
178180
case <-ctx.Done():
179181
return
@@ -183,7 +185,7 @@ func (c *Cache) run(ctx context.Context) {
183185

184186
func (c *Cache) Close() error {
185187
c.cancel()
186-
<-c.doneCh
188+
<-c.done
187189
return nil
188190
}
189191

@@ -204,8 +206,8 @@ func (c *Cache) TemplateDAUs(id uuid.UUID) codersdk.TemplateDAUsResponse {
204206
return resp
205207
}
206208

207-
// TemplateUniqueUsers returns the total number of unique users for the template,
208-
// from all the Cache data.
209+
// TemplateUniqueUsers returns the number of unique Template users
210+
// from all Cache data.
209211
func (c *Cache) TemplateUniqueUsers(id uuid.UUID) (int, bool) {
210212
m := c.templateUniqueUsers.Load()
211213
if m == nil {

coderd/metricscache/metricscache_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ func TestCache(t *testing.T) {
173173

174174
gotUniqueUsers, ok := cache.TemplateUniqueUsers(templateID)
175175
require.False(t, ok, "template shouldn't have loaded yet")
176+
require.EqualValues(t, -1, gotUniqueUsers)
176177

177178
for _, row := range tt.args.rows {
178179
row.TemplateID = templateID

coderd/templates.go

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ func (api *API) template(rw http.ResponseWriter, r *http.Request) {
7878
return
7979
}
8080

81-
httpapi.Write(rw, http.StatusOK, convertTemplate(template, count, createdByNameMap[template.ID.String()]))
81+
httpapi.Write(rw, http.StatusOK, api.convertTemplate(template, count, createdByNameMap[template.ID.String()]))
8282
}
8383

8484
func (api *API) deleteTemplate(rw http.ResponseWriter, r *http.Request) {
@@ -268,7 +268,7 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
268268
return xerrors.Errorf("get creator name: %w", err)
269269
}
270270

271-
template = convertTemplate(dbTemplate, 0, createdByNameMap[dbTemplate.ID.String()])
271+
template = api.convertTemplate(dbTemplate, 0, createdByNameMap[dbTemplate.ID.String()])
272272
return nil
273273
})
274274
if err != nil {
@@ -339,7 +339,7 @@ func (api *API) templatesByOrganization(rw http.ResponseWriter, r *http.Request)
339339
return
340340
}
341341

342-
httpapi.Write(rw, http.StatusOK, convertTemplates(templates, workspaceCounts, createdByNameMap))
342+
httpapi.Write(rw, http.StatusOK, api.convertTemplates(templates, workspaceCounts, createdByNameMap))
343343
}
344344

345345
func (api *API) templateByOrganizationAndName(rw http.ResponseWriter, r *http.Request) {
@@ -393,7 +393,7 @@ func (api *API) templateByOrganizationAndName(rw http.ResponseWriter, r *http.Re
393393
return
394394
}
395395

396-
httpapi.Write(rw, http.StatusOK, convertTemplate(template, count, createdByNameMap[template.ID.String()]))
396+
httpapi.Write(rw, http.StatusOK, api.convertTemplate(template, count, createdByNameMap[template.ID.String()]))
397397
}
398398

399399
func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
@@ -514,7 +514,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
514514
return
515515
}
516516

517-
httpapi.Write(rw, http.StatusOK, convertTemplate(updated, count, createdByNameMap[updated.ID.String()]))
517+
httpapi.Write(rw, http.StatusOK, api.convertTemplate(updated, count, createdByNameMap[updated.ID.String()]))
518518
}
519519

520520
func (api *API) templateDAUs(rw http.ResponseWriter, r *http.Request) {
@@ -683,7 +683,7 @@ func getCreatedByNamesByTemplateIDs(ctx context.Context, db database.Store, temp
683683
return creators, nil
684684
}
685685

686-
func convertTemplates(templates []database.Template, workspaceCounts []database.GetWorkspaceOwnerCountsByTemplateIDsRow, createdByNameMap map[string]string) []codersdk.Template {
686+
func (api *API) convertTemplates(templates []database.Template, workspaceCounts []database.GetWorkspaceOwnerCountsByTemplateIDsRow, createdByNameMap map[string]string) []codersdk.Template {
687687
apiTemplates := make([]codersdk.Template, 0, len(templates))
688688

689689
for _, template := range templates {
@@ -692,24 +692,27 @@ func convertTemplates(templates []database.Template, workspaceCounts []database.
692692
if workspaceCount.TemplateID.String() != template.ID.String() {
693693
continue
694694
}
695-
apiTemplates = append(apiTemplates, convertTemplate(template, uint32(workspaceCount.Count), createdByNameMap[template.ID.String()]))
695+
apiTemplates = append(apiTemplates, api.convertTemplate(template, uint32(workspaceCount.Count), createdByNameMap[template.ID.String()]))
696696
found = true
697697
break
698698
}
699699
if !found {
700-
apiTemplates = append(apiTemplates, convertTemplate(template, uint32(0), createdByNameMap[template.ID.String()]))
700+
apiTemplates = append(apiTemplates, api.convertTemplate(template, uint32(0), createdByNameMap[template.ID.String()]))
701701
}
702702
}
703703

704-
// Sort templates by WorkspaceOwnerCount DESC
704+
// Sort templates by ActiveUserCount DESC
705705
sort.SliceStable(apiTemplates, func(i, j int) bool {
706-
return apiTemplates[i].WorkspaceOwnerCount > apiTemplates[j].WorkspaceOwnerCount
706+
return apiTemplates[i].ActiveUserCount > apiTemplates[j].ActiveUserCount
707707
})
708708

709709
return apiTemplates
710710
}
711711

712-
func convertTemplate(template database.Template, workspaceOwnerCount uint32, createdByName string) codersdk.Template {
712+
func (api *API) convertTemplate(
713+
template database.Template, workspaceOwnerCount uint32, createdByName string,
714+
) codersdk.Template {
715+
activeCount, _ := api.metricsCache.TemplateUniqueUsers(template.ID)
713716
return codersdk.Template{
714717
ID: template.ID,
715718
CreatedAt: template.CreatedAt,
@@ -719,6 +722,7 @@ func convertTemplate(template database.Template, workspaceOwnerCount uint32, cre
719722
Provisioner: codersdk.ProvisionerType(template.Provisioner),
720723
ActiveVersionID: template.ActiveVersionID,
721724
WorkspaceOwnerCount: workspaceOwnerCount,
725+
ActiveUserCount: activeCount,
722726
Description: template.Description,
723727
Icon: template.Icon,
724728
MaxTTLMillis: time.Duration(template.MaxTtl).Milliseconds(),

coderd/templates_test.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,8 @@ func TestTemplateDAUs(t *testing.T) {
575575
}},
576576
})
577577
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
578+
require.Equal(t, -1, template.ActiveUserCount)
579+
578580
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
579581
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
580582
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
@@ -617,7 +619,7 @@ func TestTemplateDAUs(t *testing.T) {
617619
require.NoError(t, err)
618620
_ = sshConn.Close()
619621

620-
want := &codersdk.TemplateDAUsResponse{
622+
wantDAUs := &codersdk.TemplateDAUsResponse{
621623
Entries: []codersdk.DAUEntry{
622624
{
623625

@@ -629,12 +631,18 @@ func TestTemplateDAUs(t *testing.T) {
629631
require.Eventuallyf(t, func() bool {
630632
daus, err = client.TemplateDAUs(ctx, template.ID)
631633
require.NoError(t, err)
632-
633-
return assert.ObjectsAreEqual(want, daus)
634+
return len(daus.Entries) > 0
634635
},
635636
testutil.WaitShort, testutil.IntervalFast,
636-
"got %+v != %+v", daus, want,
637+
"template daus never loaded",
637638
)
639+
gotDAUs, err := client.TemplateDAUs(ctx, template.ID)
640+
require.NoError(t, err)
641+
require.Equal(t, gotDAUs, wantDAUs)
642+
643+
template, err = client.Template(ctx, template.ID)
644+
require.NoError(t, err)
645+
require.Equal(t, 1, template.ActiveUserCount)
638646

639647
workspaces, err = client.Workspaces(ctx, codersdk.WorkspaceFilter{})
640648
require.NoError(t, err)

codersdk/templates.go

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,22 @@ import (
1414
// Template is the JSON representation of a Coder template. This type matches the
1515
// database object for now, but is abstracted for ease of change later on.
1616
type Template struct {
17-
ID uuid.UUID `json:"id"`
18-
CreatedAt time.Time `json:"created_at"`
19-
UpdatedAt time.Time `json:"updated_at"`
20-
OrganizationID uuid.UUID `json:"organization_id"`
21-
Name string `json:"name"`
22-
Provisioner ProvisionerType `json:"provisioner"`
23-
ActiveVersionID uuid.UUID `json:"active_version_id"`
24-
WorkspaceOwnerCount uint32 `json:"workspace_owner_count"`
25-
Description string `json:"description"`
26-
Icon string `json:"icon"`
27-
MaxTTLMillis int64 `json:"max_ttl_ms"`
28-
MinAutostartIntervalMillis int64 `json:"min_autostart_interval_ms"`
29-
CreatedByID uuid.UUID `json:"created_by_id"`
30-
CreatedByName string `json:"created_by_name"`
17+
ID uuid.UUID `json:"id"`
18+
CreatedAt time.Time `json:"created_at"`
19+
UpdatedAt time.Time `json:"updated_at"`
20+
OrganizationID uuid.UUID `json:"organization_id"`
21+
Name string `json:"name"`
22+
Provisioner ProvisionerType `json:"provisioner"`
23+
ActiveVersionID uuid.UUID `json:"active_version_id"`
24+
WorkspaceOwnerCount uint32 `json:"workspace_owner_count"`
25+
// ActiveUserCount is set to -1 when loading.
26+
ActiveUserCount int `json:"active_user_count"`
27+
Description string `json:"description"`
28+
Icon string `json:"icon"`
29+
MaxTTLMillis int64 `json:"max_ttl_ms"`
30+
MinAutostartIntervalMillis int64 `json:"min_autostart_interval_ms"`
31+
CreatedByID uuid.UUID `json:"created_by_id"`
32+
CreatedByName string `json:"created_by_name"`
3133
}
3234

3335
type UpdateActiveTemplateVersion struct {

site/src/api/typesGenerated.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,7 @@ export interface Template {
367367
readonly provisioner: ProvisionerType
368368
readonly active_version_id: string
369369
readonly workspace_owner_count: number
370+
readonly active_user_count: number
370371
readonly description: string
371372
readonly icon: string
372373
readonly max_ttl_ms: number

site/src/components/TemplateStats/TemplateStats.stories.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,16 @@ export const UsedByMany = Template.bind({})
1919
UsedByMany.args = {
2020
template: {
2121
...Mocks.MockTemplate,
22-
workspace_owner_count: 15,
22+
active_user_count: 15,
23+
},
24+
activeVersion: Mocks.MockTemplateVersion,
25+
}
26+
27+
export const ActiveUsersNotLoaded = Template.bind({})
28+
ActiveUsersNotLoaded.args = {
29+
template: {
30+
...Mocks.MockTemplate,
31+
active_user_count: -1,
2332
},
2433
activeVersion: Mocks.MockTemplateVersion,
2534
}

0 commit comments

Comments
 (0)