Skip to content

Commit c5787be

Browse files
committed
Complete frontend
1 parent 055135a commit c5787be

File tree

16 files changed

+99
-61
lines changed

16 files changed

+99
-61
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/portforward.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@ import (
1212
"sync"
1313
"syscall"
1414

15-
"cdr.dev/slog"
1615
"github.com/pion/udp"
1716
"github.com/spf13/cobra"
1817
"golang.org/x/xerrors"
1918

19+
"cdr.dev/slog"
20+
2021
"github.com/coder/coder/agent"
2122
"github.com/coder/coder/cli/cliui"
2223
"github.com/coder/coder/codersdk"

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: 11 additions & 11 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,7 +146,7 @@ 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()
@@ -173,7 +173,7 @@ func (c *Cache) run(ctx context.Context) {
173173

174174
select {
175175
case <-ticker.C:
176-
case <-c.doneCh:
176+
case <-c.done:
177177
return
178178
case <-ctx.Done():
179179
return
@@ -183,29 +183,29 @@ func (c *Cache) run(ctx context.Context) {
183183

184184
func (c *Cache) Close() error {
185185
c.cancel()
186-
<-c.doneCh
186+
<-c.done
187187
return nil
188188
}
189189

190190
// TemplateDAUs returns an empty response if the template doesn't have users
191191
// or is loading for the first time.
192-
func (c *Cache) TemplateDAUs(id uuid.UUID) codersdk.TemplateDAUsResponse {
192+
func (c *Cache) TemplateDAUs(id uuid.UUID) (*codersdk.TemplateDAUsResponse, bool) {
193193
m := c.templateDAUResponses.Load()
194194
if m == nil {
195195
// Data loading.
196-
return codersdk.TemplateDAUsResponse{}
196+
return nil, false
197197
}
198198

199199
resp, ok := (*m)[id]
200200
if !ok {
201201
// Probably no data.
202-
return codersdk.TemplateDAUsResponse{}
202+
return nil, false
203203
}
204-
return resp
204+
return &resp, true
205205
}
206206

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

coderd/metricscache/metricscache_test.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,22 +173,25 @@ 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
179180
db.InsertAgentStat(context.Background(), row)
180181
}
181182

182183
require.Eventuallyf(t, func() bool {
183-
return len(cache.TemplateDAUs(templateID).Entries) > 0
184+
_, ok := cache.TemplateDAUs(templateID)
185+
return ok
184186
}, testutil.WaitShort, testutil.IntervalMedium,
185187
"TemplateDAUs never populated",
186188
)
187189

188190
gotUniqueUsers, ok = cache.TemplateUniqueUsers(templateID)
189191
require.True(t, ok)
190192

191-
gotEntries := cache.TemplateDAUs(templateID)
193+
gotEntries, ok := cache.TemplateDAUs(templateID)
194+
require.True(t, ok)
192195
require.Equal(t, tt.want.entries, gotEntries.Entries)
193196
require.Equal(t, tt.want.uniqueUsers, gotUniqueUsers)
194197
})

coderd/templates.go

Lines changed: 16 additions & 12 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) {
@@ -524,7 +524,7 @@ func (api *API) templateDAUs(rw http.ResponseWriter, r *http.Request) {
524524
return
525525
}
526526

527-
resp := api.metricsCache.TemplateDAUs(template.ID)
527+
resp, _ := api.metricsCache.TemplateDAUs(template.ID)
528528
if resp.Entries == nil {
529529
resp.Entries = []codersdk.DAUEntry{}
530530
}
@@ -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

0 commit comments

Comments
 (0)