Skip to content

Commit 05b98fd

Browse files
committed
chore: implement cli list organization members
1 parent 08e4131 commit 05b98fd

File tree

5 files changed

+105
-10
lines changed

5 files changed

+105
-10
lines changed

cli/organization.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,10 @@ import (
1818

1919
func (r *RootCmd) organizations() *serpent.Command {
2020
cmd := &serpent.Command{
21-
Annotations: workspaceCommand,
22-
Use: "organizations [subcommand]",
23-
Short: "Organization related commands",
24-
Aliases: []string{"organization", "org", "orgs"},
25-
Hidden: true, // Hidden until these commands are complete.
21+
Use: "organizations [subcommand]",
22+
Short: "Organization related commands",
23+
Aliases: []string{"organization", "org", "orgs"},
24+
Hidden: true, // Hidden until these commands are complete.
2625
Handler: func(inv *serpent.Invocation) error {
2726
return inv.Command.HelpHandler(inv)
2827
},
@@ -31,6 +30,7 @@ func (r *RootCmd) organizations() *serpent.Command {
3130
r.switchOrganization(),
3231
r.createOrganization(),
3332
r.organizationRoles(),
33+
r.organizationMembers(),
3434
},
3535
}
3636

cli/organizationmembers.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package cli
2+
3+
import (
4+
"fmt"
5+
6+
"golang.org/x/xerrors"
7+
8+
"github.com/coder/coder/v2/cli/cliui"
9+
"github.com/coder/coder/v2/codersdk"
10+
"github.com/coder/serpent"
11+
)
12+
13+
func (r *RootCmd) organizationMembers() *serpent.Command {
14+
formatter := cliui.NewOutputFormatter(
15+
cliui.TableFormat([]codersdk.OrganizationMemberWithName{}, []string{"username", "organization_roles"}),
16+
cliui.JSONFormat(),
17+
)
18+
19+
client := new(codersdk.Client)
20+
cmd := &serpent.Command{
21+
Use: "members",
22+
Short: "List all organization members",
23+
Aliases: []string{"member"},
24+
Middleware: serpent.Chain(
25+
serpent.RequireNArgs(0),
26+
r.InitClient(client),
27+
),
28+
Handler: func(inv *serpent.Invocation) error {
29+
ctx := inv.Context()
30+
organization, err := CurrentOrganization(r, inv, client)
31+
if err != nil {
32+
return err
33+
}
34+
35+
res, err := client.OrganizationMembers(ctx, organization.ID)
36+
if err != nil {
37+
return xerrors.Errorf("fetch members: %w", err)
38+
}
39+
40+
out, err := formatter.Format(inv.Context(), res)
41+
if err != nil {
42+
return err
43+
}
44+
45+
_, err = fmt.Fprintln(inv.Stdout, out)
46+
return err
47+
},
48+
}
49+
formatter.AttachOptions(&cmd.Options)
50+
51+
return cmd
52+
}

cli/organizationmembers_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package cli_test
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
7+
"github.com/stretchr/testify/require"
8+
9+
"github.com/coder/coder/v2/cli/clitest"
10+
"github.com/coder/coder/v2/coderd/coderdtest"
11+
"github.com/coder/coder/v2/coderd/rbac"
12+
"github.com/coder/coder/v2/testutil"
13+
)
14+
15+
func TestListOrganizationMembers(t *testing.T) {
16+
t.Parallel()
17+
18+
t.Run("OK", func(t *testing.T) {
19+
t.Parallel()
20+
21+
ownerClient := coderdtest.New(t, &coderdtest.Options{})
22+
owner := coderdtest.CreateFirstUser(t, ownerClient)
23+
client, user := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID, rbac.RoleUserAdmin())
24+
25+
ctx := testutil.Context(t, testutil.WaitMedium)
26+
inv, root := clitest.New(t, "organization", "members", "-c", "user_id,username,roles")
27+
clitest.SetupConfig(t, client, root)
28+
29+
buf := new(bytes.Buffer)
30+
inv.Stdout = buf
31+
err := inv.WithContext(ctx).Run()
32+
require.NoError(t, err)
33+
require.Contains(t, buf.String(), user.Username)
34+
require.Contains(t, buf.String(), owner.UserID.String())
35+
})
36+
}

codersdk/organizations.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,11 @@ type Organization struct {
5050
}
5151

5252
type OrganizationMember struct {
53-
UserID uuid.UUID `db:"user_id" json:"user_id" format:"uuid"`
54-
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id" format:"uuid"`
55-
CreatedAt time.Time `db:"created_at" json:"created_at" format:"date-time"`
56-
UpdatedAt time.Time `db:"updated_at" json:"updated_at" format:"date-time"`
57-
Roles []SlimRole `db:"roles" json:"roles"`
53+
UserID uuid.UUID `table:"user id" json:"user_id" format:"uuid"`
54+
OrganizationID uuid.UUID `table:"organization id" json:"organization_id" format:"uuid"`
55+
CreatedAt time.Time `table:"created at" json:"created_at" format:"date-time"`
56+
UpdatedAt time.Time `table:"updated at" json:"updated_at" format:"date-time"`
57+
Roles []SlimRole `table:"organization_roles" json:"roles"`
5858
}
5959

6060
type OrganizationMemberWithName struct {

codersdk/roles.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ type SlimRole struct {
1919
OrganizationID string `json:"organization_id,omitempty"`
2020
}
2121

22+
func (s SlimRole) String() string {
23+
if s.DisplayName != "" {
24+
return s.DisplayName
25+
}
26+
return s.Name
27+
}
28+
2229
type AssignableRoles struct {
2330
Role `table:"r,recursive_inline"`
2431
Assignable bool `json:"assignable" table:"assignable"`

0 commit comments

Comments
 (0)