Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f8b97d9

Browse files
committed
implement more general organizations show
1 parent cad75c2 commit f8b97d9

File tree

5 files changed

+104
-24
lines changed

5 files changed

+104
-24
lines changed

cli/organization.go

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cli
22

33
import (
44
"fmt"
5+
"strings"
56

67
"github.com/coder/coder/v2/cli/clibase"
78
"github.com/coder/coder/v2/cli/cliui"
@@ -29,29 +30,28 @@ func (r *RootCmd) organizations() *clibase.Cmd {
2930

3031
func (r *RootCmd) currentOrganization() *clibase.Cmd {
3132
var (
32-
client = new(codersdk.Client)
33-
formatter = cliui.NewOutputFormatter(
33+
stringFormat func(orgs []codersdk.Organization) (string, error)
34+
client = new(codersdk.Client)
35+
formatter = cliui.NewOutputFormatter(
3436
cliui.ChangeFormatterData(cliui.TextFormat(), func(data any) (any, error) {
3537
typed, ok := data.([]codersdk.Organization)
3638
if !ok {
3739
// This should never happen
3840
return "", fmt.Errorf("expected []Organization, got %T", data)
3941
}
40-
if len(typed) != 1 {
41-
return "", fmt.Errorf("expected 1 organization, got %d", len(typed))
42-
}
43-
return fmt.Sprintf("Current organization: %s (%s)\n", typed[0].Name, typed[0].ID.String()), nil
42+
return stringFormat(typed)
4443
}),
4544
cliui.TableFormat([]codersdk.Organization{}, []string{"id", "name", "default"}),
4645
cliui.JSONFormat(),
4746
)
4847
onlyID = false
4948
)
5049
cmd := &clibase.Cmd{
51-
Use: "current",
52-
Short: "Show the current selected organization the cli will use.",
50+
Use: "show [current|me|uuid]",
51+
Short: "Show organization information. By default, if no argument is provided, the current cli configured organization is shown.",
5352
Middleware: clibase.Chain(
5453
r.InitClient(client),
54+
clibase.RequireRangeArgs(0, 1),
5555
),
5656
Options: clibase.OptionSet{
5757
{
@@ -63,15 +63,60 @@ func (r *RootCmd) currentOrganization() *clibase.Cmd {
6363
},
6464
},
6565
Handler: func(inv *clibase.Invocation) error {
66-
org, err := CurrentOrganization(r, inv, client)
67-
if err != nil {
68-
return err
66+
orgArg := "current"
67+
if len(inv.Args) == 1 {
68+
orgArg = inv.Args[0]
69+
}
70+
71+
var orgs []codersdk.Organization
72+
var err error
73+
switch strings.ToLower(orgArg) {
74+
case "current":
75+
stringFormat = func(orgs []codersdk.Organization) (string, error) {
76+
if len(orgs) != 1 {
77+
return "", fmt.Errorf("expected 1 organization, got %d", len(orgs))
78+
}
79+
return fmt.Sprintf("Current CLI Organization: %s (%s)\n", orgs[0].Name, orgs[0].ID.String()), nil
80+
}
81+
org, err := CurrentOrganization(r, inv, client)
82+
if err != nil {
83+
return err
84+
}
85+
orgs = []codersdk.Organization{org}
86+
case "me":
87+
stringFormat = func(orgs []codersdk.Organization) (string, error) {
88+
var str strings.Builder
89+
_, _ = fmt.Fprint(&str, "Organizations you are a member of:\n")
90+
for _, org := range orgs {
91+
_, _ = fmt.Fprintf(&str, "\t%s (%s)\n", org.Name, org.ID.String())
92+
}
93+
return str.String(), nil
94+
}
95+
orgs, err = client.OrganizationsByUser(inv.Context(), codersdk.Me)
96+
if err != nil {
97+
return err
98+
}
99+
default:
100+
stringFormat = func(orgs []codersdk.Organization) (string, error) {
101+
if len(orgs) != 1 {
102+
return "", fmt.Errorf("expected 1 organization, got %d", len(orgs))
103+
}
104+
return fmt.Sprintf("Organization: %s (%s)\n", orgs[0].Name, orgs[0].ID.String()), nil
105+
}
106+
// This works for a uuid or a name
107+
org, err := client.OrganizationByName(inv.Context(), orgArg)
108+
if err != nil {
109+
return err
110+
}
111+
orgs = []codersdk.Organization{org}
69112
}
70113

71114
if onlyID {
72-
_, _ = fmt.Fprintf(inv.Stdout, "%s\n", org.ID)
115+
for _, org := range orgs {
116+
_, _ = fmt.Fprintf(inv.Stdout, "%s\n", org.ID)
117+
}
73118
} else {
74-
out, err := formatter.Format(inv.Context(), []codersdk.Organization{org})
119+
out, err := formatter.Format(inv.Context(), orgs)
75120
if err != nil {
76121
return err
77122
}

coderd/httpmw/organizationparam.go

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@ package httpmw
22

33
import (
44
"context"
5+
"fmt"
56
"net/http"
67

8+
"github.com/go-chi/chi/v5"
9+
"github.com/google/uuid"
10+
711
"github.com/coder/coder/v2/coderd/database"
812
"github.com/coder/coder/v2/coderd/database/dbauthz"
913
"github.com/coder/coder/v2/coderd/httpapi"
@@ -40,19 +44,34 @@ func ExtractOrganizationParam(db database.Store) func(http.Handler) http.Handler
4044
return func(next http.Handler) http.Handler {
4145
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
4246
ctx := r.Context()
43-
orgID, ok := ParseUUIDParam(rw, r, "organization")
44-
if !ok {
47+
arg := chi.URLParam(r, "organization")
48+
if arg == "" {
49+
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
50+
Message: "\"organization\" must be provided.",
51+
})
4552
return
4653
}
4754

48-
organization, err := db.GetOrganizationByID(ctx, orgID)
55+
var organization database.Organization
56+
var err error
57+
// Try by name or uuid.
58+
id, err := uuid.Parse(arg)
59+
if err == nil {
60+
organization, err = db.GetOrganizationByID(ctx, id)
61+
} else {
62+
organization, err = db.GetOrganizationByName(ctx, arg)
63+
}
4964
if httpapi.Is404Error(err) {
5065
httpapi.ResourceNotFound(rw)
66+
httpapi.Write(ctx, rw, http.StatusNotFound, codersdk.Response{
67+
Message: fmt.Sprintf("Organization %q not found.", arg),
68+
Detail: "Provide either the organization id or name.",
69+
})
5170
return
5271
}
5372
if err != nil {
5473
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
55-
Message: "Internal error fetching organization.",
74+
Message: fmt.Sprintf("Internal error fetching organization %q.", arg),
5675
Detail: err.Error(),
5776
})
5877
return

coderd/httpmw/organizationparam_test.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ func TestOrganizationParam(t *testing.T) {
103103
rtr.ServeHTTP(rw, r)
104104
res := rw.Result()
105105
defer res.Body.Close()
106-
require.Equal(t, http.StatusBadRequest, res.StatusCode)
106+
require.Equal(t, http.StatusNotFound, res.StatusCode)
107107
})
108108

109109
t.Run("NotInOrganization", func(t *testing.T) {
@@ -160,8 +160,6 @@ func TestOrganizationParam(t *testing.T) {
160160
})
161161
require.NoError(t, err)
162162

163-
chi.RouteContext(r.Context()).URLParams.Add("organization", organization.ID.String())
164-
chi.RouteContext(r.Context()).URLParams.Add("user", user.ID.String())
165163
rtr.Use(
166164
httpmw.ExtractAPIKeyMW(httpmw.ExtractAPIKeyConfig{
167165
DB: db,
@@ -194,9 +192,21 @@ func TestOrganizationParam(t *testing.T) {
194192
assert.NotEmpty(t, orgMem.OrganizationMember.UserID)
195193
assert.NotEmpty(t, orgMem.OrganizationMember.Roles)
196194
})
195+
196+
// Try by ID
197+
chi.RouteContext(r.Context()).URLParams.Add("organization", organization.ID.String())
198+
chi.RouteContext(r.Context()).URLParams.Add("user", user.ID.String())
197199
rtr.ServeHTTP(rw, r)
198200
res := rw.Result()
199201
defer res.Body.Close()
200-
require.Equal(t, http.StatusOK, res.StatusCode)
202+
require.Equal(t, http.StatusOK, res.StatusCode, "by id")
203+
204+
// Try by name
205+
chi.RouteContext(r.Context()).URLParams.Add("organization", organization.Name)
206+
chi.RouteContext(r.Context()).URLParams.Add("user", user.ID.String())
207+
rtr.ServeHTTP(rw, r)
208+
res = rw.Result()
209+
defer res.Body.Close()
210+
require.Equal(t, http.StatusOK, res.StatusCode, "by name")
201211
})
202212
}

codersdk/organizations.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,8 @@ type CreateWorkspaceRequest struct {
153153
AutomaticUpdates AutomaticUpdates `json:"automatic_updates,omitempty"`
154154
}
155155

156-
func (c *Client) Organization(ctx context.Context, id uuid.UUID) (Organization, error) {
157-
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/organizations/%s", id.String()), nil)
156+
func (c *Client) OrganizationByName(ctx context.Context, name string) (Organization, error) {
157+
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/organizations/%s", name), nil)
158158
if err != nil {
159159
return Organization{}, xerrors.Errorf("execute request: %w", err)
160160
}
@@ -168,6 +168,12 @@ func (c *Client) Organization(ctx context.Context, id uuid.UUID) (Organization,
168168
return organization, json.NewDecoder(res.Body).Decode(&organization)
169169
}
170170

171+
func (c *Client) Organization(ctx context.Context, id uuid.UUID) (Organization, error) {
172+
// OrganizationByName uses the exact same endpoint. It accepts a name or uuid.
173+
// We just provide this function for type safety.
174+
return c.OrganizationByName(ctx, id.String())
175+
}
176+
171177
// ProvisionerDaemons returns provisioner daemons available.
172178
func (c *Client) ProvisionerDaemons(ctx context.Context) ([]ProvisionerDaemon, error) {
173179
res, err := c.Request(ctx, http.MethodGet,

codersdk/users.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -573,7 +573,7 @@ func (c *Client) OrganizationsByUser(ctx context.Context, user string) ([]Organi
573573
return orgs, json.NewDecoder(res.Body).Decode(&orgs)
574574
}
575575

576-
func (c *Client) OrganizationByName(ctx context.Context, user string, name string) (Organization, error) {
576+
func (c *Client) OrganizationByUserAndName(ctx context.Context, user string, name string) (Organization, error) {
577577
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s/organizations/%s", user, name), nil)
578578
if err != nil {
579579
return Organization{}, err

0 commit comments

Comments
 (0)