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 ", 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 }