coder/cli/userlist.go

180 lines
3.7 KiB
Go

package cli
import (
"context"
"fmt"
"time"
"github.com/jedib0t/go-pretty/v6/table"
"golang.org/x/xerrors"
"github.com/coder/coder/v2/cli/cliui"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/serpent"
)
func (r *RootCmd) userList() *serpent.Command {
formatter := cliui.NewOutputFormatter(
cliui.TableFormat([]codersdk.User{}, []string{"username", "email", "created_at", "status"}),
cliui.JSONFormat(),
)
client := new(codersdk.Client)
cmd := &serpent.Command{
Use: "list",
Aliases: []string{"ls"},
Middleware: serpent.Chain(
serpent.RequireNArgs(0),
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
res, err := client.Users(inv.Context(), codersdk.UsersRequest{})
if err != nil {
return err
}
out, err := formatter.Format(inv.Context(), res.Users)
if err != nil {
return err
}
_, err = fmt.Fprintln(inv.Stdout, out)
return err
},
}
formatter.AttachOptions(&cmd.Options)
return cmd
}
func (r *RootCmd) userSingle() *serpent.Command {
formatter := cliui.NewOutputFormatter(
&userShowFormat{},
cliui.JSONFormat(),
)
client := new(codersdk.Client)
cmd := &serpent.Command{
Use: "show <username|user_id|'me'>",
Short: "Show a single user. Use 'me' to indicate the currently authenticated user.",
Long: formatExamples(
example{
Command: "coder users show me",
},
),
Middleware: serpent.Chain(
serpent.RequireNArgs(1),
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
user, err := client.User(inv.Context(), inv.Args[0])
if err != nil {
return err
}
orgNames := make([]string, len(user.OrganizationIDs))
for i, orgID := range user.OrganizationIDs {
org, err := client.Organization(inv.Context(), orgID)
if err != nil {
return xerrors.Errorf("get organization %q: %w", orgID.String(), err)
}
orgNames[i] = org.Name
}
out, err := formatter.Format(inv.Context(), userWithOrgNames{
User: user,
OrganizationNames: orgNames,
})
if err != nil {
return err
}
_, err = fmt.Fprintln(inv.Stdout, out)
return err
},
}
formatter.AttachOptions(&cmd.Options)
return cmd
}
type userWithOrgNames struct {
codersdk.User
OrganizationNames []string `json:"organization_names"`
}
type userShowFormat struct{}
var _ cliui.OutputFormat = &userShowFormat{}
// ID implements OutputFormat.
func (*userShowFormat) ID() string {
return "table"
}
// AttachOptions implements OutputFormat.
func (*userShowFormat) AttachOptions(_ *serpent.OptionSet) {}
// Format implements OutputFormat.
func (*userShowFormat) Format(_ context.Context, out interface{}) (string, error) {
user, ok := out.(userWithOrgNames)
if !ok {
return "", xerrors.Errorf("expected type %T, got %T", user, out)
}
tw := cliui.Table()
addRow := func(name string, value interface{}) {
key := ""
if name != "" {
key = name + ":"
}
tw.AppendRow(table.Row{
key, value,
})
}
// Add rows for each of the user's fields.
addRow("ID", user.ID.String())
addRow("Username", user.Username)
addRow("Email", user.Email)
addRow("Status", user.Status)
addRow("Created At", user.CreatedAt.Format(time.Stamp))
addRow("", "")
firstRole := true
for _, role := range user.Roles {
if role.DisplayName == "" {
// Skip roles with no display name.
continue
}
key := ""
if firstRole {
key = "Roles"
firstRole = false
}
addRow(key, role.DisplayName)
}
if firstRole {
addRow("Roles", "(none)")
}
addRow("", "")
firstOrg := true
for _, orgName := range user.OrganizationNames {
key := ""
if firstOrg {
key = "Organizations"
firstOrg = false
}
addRow(key, orgName)
}
if firstOrg {
addRow("Organizations", "(none)")
}
return tw.Render(), nil
}