Skip to content

Commit 5a61631

Browse files
committed
feat(cli): extract handler for list cmd, add next start / stop
1 parent ed7e43b commit 5a61631

File tree

1 file changed

+70
-53
lines changed

1 file changed

+70
-53
lines changed

cli/list.go

+70-53
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
package cli
22

33
import (
4+
"context"
45
"fmt"
6+
"io"
57
"strconv"
68
"time"
79

8-
"github.com/google/uuid"
9-
1010
"github.com/coder/pretty"
1111

1212
"github.com/coder/coder/v2/cli/clibase"
@@ -31,57 +31,62 @@ type workspaceListRow struct {
3131
LastBuilt string `json:"-" table:"last built"`
3232
Outdated bool `json:"-" table:"outdated"`
3333
StartsAt string `json:"-" table:"starts at"`
34+
StartsNext string `json:"-" table:"starts next"`
3435
StopsAfter string `json:"-" table:"stops after"`
36+
StopsNext string `json:"-" table:"stops next"`
3537
DailyCost string `json:"-" table:"daily cost"`
3638
}
3739

38-
func workspaceListRowFromWorkspace(now time.Time, usersByID map[uuid.UUID]codersdk.User, workspace codersdk.Workspace) workspaceListRow {
40+
func workspaceListRowFromWorkspace(now time.Time, workspace codersdk.Workspace) workspaceListRow {
3941
status := codersdk.WorkspaceDisplayStatus(workspace.LatestBuild.Job.Status, workspace.LatestBuild.Transition)
4042

4143
lastBuilt := now.UTC().Sub(workspace.LatestBuild.Job.CreatedAt).Truncate(time.Second)
4244
autostartDisplay := "-"
45+
nextStartDisplay := "-"
4346
if !ptr.NilOrEmpty(workspace.AutostartSchedule) {
4447
if sched, err := cron.Weekly(*workspace.AutostartSchedule); err == nil {
4548
autostartDisplay = fmt.Sprintf("%s %s (%s)", sched.Time(), sched.DaysOfWeek(), sched.Location())
49+
nextStartDisplay = relative(sched.Next(now).Sub(now))
4650
}
4751
}
4852

4953
autostopDisplay := "-"
54+
nextStopDisplay := "-"
5055
if !ptr.NilOrZero(workspace.TTLMillis) {
5156
dur := time.Duration(*workspace.TTLMillis) * time.Millisecond
5257
autostopDisplay = durationDisplay(dur)
5358
if !workspace.LatestBuild.Deadline.IsZero() && workspace.LatestBuild.Deadline.Time.After(now) && status == "Running" {
5459
remaining := time.Until(workspace.LatestBuild.Deadline.Time)
55-
autostopDisplay = fmt.Sprintf("%s (%s)", autostopDisplay, relative(remaining))
60+
nextStopDisplay = fmt.Sprintf("%s (%s)", autostopDisplay, relative(remaining))
5661
}
5762
}
5863

5964
healthy := ""
6065
if status == "Starting" || status == "Started" {
6166
healthy = strconv.FormatBool(workspace.Health.Healthy)
6267
}
63-
user := usersByID[workspace.OwnerID]
6468
return workspaceListRow{
6569
Workspace: workspace,
66-
WorkspaceName: user.Username + "/" + workspace.Name,
70+
WorkspaceName: workspace.OwnerName + "/" + workspace.Name,
6771
Template: workspace.TemplateName,
6872
Status: status,
6973
Healthy: healthy,
7074
LastBuilt: durationDisplay(lastBuilt),
7175
Outdated: workspace.Outdated,
7276
StartsAt: autostartDisplay,
77+
StartsNext: nextStartDisplay,
7378
StopsAfter: autostopDisplay,
79+
StopsNext: nextStopDisplay,
7480
DailyCost: strconv.Itoa(int(workspace.LatestBuild.DailyCost)),
7581
}
7682
}
7783

7884
func (r *RootCmd) list() *clibase.Cmd {
7985
var (
80-
all bool
81-
defaultQuery = "owner:me"
82-
searchQuery string
83-
displayWorkspaces []workspaceListRow
84-
formatter = cliui.NewOutputFormatter(
86+
all bool
87+
defaultQuery = "owner:me"
88+
searchQuery string
89+
formatter = cliui.NewOutputFormatter(
8590
cliui.TableFormat(
8691
[]workspaceListRow{},
8792
[]string{
@@ -109,48 +114,17 @@ func (r *RootCmd) list() *clibase.Cmd {
109114
r.InitClient(client),
110115
),
111116
Handler: func(inv *clibase.Invocation) error {
112-
filter := codersdk.WorkspaceFilter{
113-
FilterQuery: searchQuery,
114-
}
115-
if all && searchQuery == defaultQuery {
116-
filter.FilterQuery = ""
117-
}
118-
119-
res, err := client.Workspaces(inv.Context(), filter)
120-
if err != nil {
121-
return err
122-
}
123-
if len(res.Workspaces) == 0 {
124-
pretty.Fprintf(inv.Stderr, cliui.DefaultStyles.Prompt, "No workspaces found! Create one:\n")
125-
_, _ = fmt.Fprintln(inv.Stderr)
126-
_, _ = fmt.Fprintln(inv.Stderr, " "+pretty.Sprint(cliui.DefaultStyles.Code, "coder create <name>"))
127-
_, _ = fmt.Fprintln(inv.Stderr)
128-
return nil
129-
}
130-
131-
userRes, err := client.Users(inv.Context(), codersdk.UsersRequest{})
132-
if err != nil {
133-
return err
134-
}
135-
136-
usersByID := map[uuid.UUID]codersdk.User{}
137-
for _, user := range userRes.Users {
138-
usersByID[user.ID] = user
139-
}
140-
141-
now := time.Now()
142-
displayWorkspaces = make([]workspaceListRow, len(res.Workspaces))
143-
for i, workspace := range res.Workspaces {
144-
displayWorkspaces[i] = workspaceListRowFromWorkspace(now, usersByID, workspace)
145-
}
146-
147-
out, err := formatter.Format(inv.Context(), displayWorkspaces)
148-
if err != nil {
149-
return err
150-
}
151-
152-
_, err = fmt.Fprintln(inv.Stdout, out)
153-
return err
117+
return handleList(
118+
inv.Context(),
119+
handleListArgs{
120+
formatter: formatter,
121+
client: client,
122+
searchQuery: searchQuery,
123+
all: all,
124+
stdout: inv.Stdout,
125+
stderr: inv.Stderr,
126+
},
127+
)
154128
},
155129
}
156130
cmd.Options = clibase.OptionSet{
@@ -172,3 +146,46 @@ func (r *RootCmd) list() *clibase.Cmd {
172146
formatter.AttachOptions(&cmd.Options)
173147
return cmd
174148
}
149+
150+
type handleListArgs struct {
151+
formatter *cliui.OutputFormatter
152+
client *codersdk.Client
153+
searchQuery string
154+
all bool
155+
stdout, stderr io.Writer
156+
}
157+
158+
func handleList(ctx context.Context, args handleListArgs) error {
159+
filter := codersdk.WorkspaceFilter{
160+
FilterQuery: args.searchQuery,
161+
}
162+
if args.all && args.searchQuery == "owner:me" {
163+
filter.FilterQuery = ""
164+
}
165+
166+
res, err := args.client.Workspaces(ctx, filter)
167+
if err != nil {
168+
return err
169+
}
170+
if len(res.Workspaces) == 0 {
171+
pretty.Fprintf(args.stderr, cliui.DefaultStyles.Prompt, "No workspaces found! Create one:\n")
172+
_, _ = fmt.Fprintln(args.stderr)
173+
_, _ = fmt.Fprintln(args.stderr, " "+pretty.Sprint(cliui.DefaultStyles.Code, "coder create <name>"))
174+
_, _ = fmt.Fprintln(args.stderr)
175+
return nil
176+
}
177+
178+
now := time.Now()
179+
displayWorkspaces := make([]workspaceListRow, len(res.Workspaces))
180+
for i, workspace := range res.Workspaces {
181+
displayWorkspaces[i] = workspaceListRowFromWorkspace(now, workspace)
182+
}
183+
184+
out, err := args.formatter.Format(ctx, displayWorkspaces)
185+
if err != nil {
186+
return err
187+
}
188+
189+
_, err = fmt.Fprintln(args.stdout, out)
190+
return err
191+
}

0 commit comments

Comments
 (0)