mirror of https://github.com/coder/coder.git
feat: implement organization context in the cli (#12259)
* feat: implement organization context in the cli `coder org show current`
This commit is contained in:
parent
f44c89d200
commit
d2998c6b7b
|
@ -70,6 +70,14 @@ func (r Root) PostgresPort() File {
|
|||
// File provides convenience methods for interacting with *os.File.
|
||||
type File string
|
||||
|
||||
func (f File) Exists() bool {
|
||||
if f == "" {
|
||||
return false
|
||||
}
|
||||
_, err := os.Stat(string(f))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// Delete deletes the file.
|
||||
func (f File) Delete() error {
|
||||
if f == "" {
|
||||
|
|
|
@ -43,7 +43,7 @@ func (r *RootCmd) create() *clibase.Cmd {
|
|||
),
|
||||
Middleware: clibase.Chain(r.InitClient(client)),
|
||||
Handler: func(inv *clibase.Invocation) error {
|
||||
organization, err := CurrentOrganization(inv, client)
|
||||
organization, err := CurrentOrganization(r, inv, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/coder/coder/v2/cli/clibase"
|
||||
"github.com/coder/coder/v2/cli/cliui"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
)
|
||||
|
||||
func (r *RootCmd) organizations() *clibase.Cmd {
|
||||
cmd := &clibase.Cmd{
|
||||
Annotations: workspaceCommand,
|
||||
Use: "organizations [subcommand]",
|
||||
Short: "Organization related commands",
|
||||
Aliases: []string{"organization", "org", "orgs"},
|
||||
Hidden: true, // Hidden until these commands are complete.
|
||||
Handler: func(inv *clibase.Invocation) error {
|
||||
return inv.Command.HelpHandler(inv)
|
||||
},
|
||||
Children: []*clibase.Cmd{
|
||||
r.currentOrganization(),
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Options = clibase.OptionSet{}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (r *RootCmd) currentOrganization() *clibase.Cmd {
|
||||
var (
|
||||
stringFormat func(orgs []codersdk.Organization) (string, error)
|
||||
client = new(codersdk.Client)
|
||||
formatter = cliui.NewOutputFormatter(
|
||||
cliui.ChangeFormatterData(cliui.TextFormat(), func(data any) (any, error) {
|
||||
typed, ok := data.([]codersdk.Organization)
|
||||
if !ok {
|
||||
// This should never happen
|
||||
return "", fmt.Errorf("expected []Organization, got %T", data)
|
||||
}
|
||||
return stringFormat(typed)
|
||||
}),
|
||||
cliui.TableFormat([]codersdk.Organization{}, []string{"id", "name", "default"}),
|
||||
cliui.JSONFormat(),
|
||||
)
|
||||
onlyID = false
|
||||
)
|
||||
cmd := &clibase.Cmd{
|
||||
Use: "show [current|me|uuid]",
|
||||
Short: "Show the organization, if no argument is given, the organization currently in use will be shown.",
|
||||
Middleware: clibase.Chain(
|
||||
r.InitClient(client),
|
||||
clibase.RequireRangeArgs(0, 1),
|
||||
),
|
||||
Options: clibase.OptionSet{
|
||||
{
|
||||
Name: "only-id",
|
||||
Description: "Only print the organization ID.",
|
||||
Required: false,
|
||||
Flag: "only-id",
|
||||
Value: clibase.BoolOf(&onlyID),
|
||||
},
|
||||
},
|
||||
Handler: func(inv *clibase.Invocation) error {
|
||||
orgArg := "current"
|
||||
if len(inv.Args) >= 1 {
|
||||
orgArg = inv.Args[0]
|
||||
}
|
||||
|
||||
var orgs []codersdk.Organization
|
||||
var err error
|
||||
switch strings.ToLower(orgArg) {
|
||||
case "current":
|
||||
stringFormat = func(orgs []codersdk.Organization) (string, error) {
|
||||
if len(orgs) != 1 {
|
||||
return "", fmt.Errorf("expected 1 organization, got %d", len(orgs))
|
||||
}
|
||||
return fmt.Sprintf("Current CLI Organization: %s (%s)\n", orgs[0].Name, orgs[0].ID.String()), nil
|
||||
}
|
||||
org, err := CurrentOrganization(r, inv, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
orgs = []codersdk.Organization{org}
|
||||
case "me":
|
||||
stringFormat = func(orgs []codersdk.Organization) (string, error) {
|
||||
var str strings.Builder
|
||||
_, _ = fmt.Fprint(&str, "Organizations you are a member of:\n")
|
||||
for _, org := range orgs {
|
||||
_, _ = fmt.Fprintf(&str, "\t%s (%s)\n", org.Name, org.ID.String())
|
||||
}
|
||||
return str.String(), nil
|
||||
}
|
||||
orgs, err = client.OrganizationsByUser(inv.Context(), codersdk.Me)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
stringFormat = func(orgs []codersdk.Organization) (string, error) {
|
||||
if len(orgs) != 1 {
|
||||
return "", fmt.Errorf("expected 1 organization, got %d", len(orgs))
|
||||
}
|
||||
return fmt.Sprintf("Organization: %s (%s)\n", orgs[0].Name, orgs[0].ID.String()), nil
|
||||
}
|
||||
// This works for a uuid or a name
|
||||
org, err := client.OrganizationByName(inv.Context(), orgArg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
orgs = []codersdk.Organization{org}
|
||||
}
|
||||
|
||||
if onlyID {
|
||||
for _, org := range orgs {
|
||||
_, _ = fmt.Fprintf(inv.Stdout, "%s\n", org.ID)
|
||||
}
|
||||
} else {
|
||||
out, err := formatter.Format(inv.Context(), orgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = fmt.Fprint(inv.Stdout, out)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
formatter.AttachOptions(&cmd.Options)
|
||||
|
||||
return cmd
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package cli_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/v2/cli/clitest"
|
||||
"github.com/coder/coder/v2/coderd/coderdtest"
|
||||
"github.com/coder/coder/v2/coderd/rbac"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/pty/ptytest"
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
)
|
||||
|
||||
func TestCurrentOrganization(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("OnlyID", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ownerClient := coderdtest.New(t, nil)
|
||||
first := coderdtest.CreateFirstUser(t, ownerClient)
|
||||
// Owner is required to make orgs
|
||||
client, _ := coderdtest.CreateAnotherUser(t, ownerClient, first.OrganizationID, rbac.RoleOwner())
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitMedium)
|
||||
orgs := []string{"foo", "bar"}
|
||||
for _, orgName := range orgs {
|
||||
_, err := client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{
|
||||
Name: orgName,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
inv, root := clitest.New(t, "organizations", "show", "--only-id")
|
||||
clitest.SetupConfig(t, client, root)
|
||||
pty := ptytest.New(t).Attach(inv)
|
||||
errC := make(chan error)
|
||||
go func() {
|
||||
errC <- inv.Run()
|
||||
}()
|
||||
require.NoError(t, <-errC)
|
||||
pty.ExpectMatch(first.OrganizationID.String())
|
||||
})
|
||||
}
|
41
cli/root.go
41
cli/root.go
|
@ -94,6 +94,7 @@ func (r *RootCmd) Core() []*clibase.Cmd {
|
|||
r.tokens(),
|
||||
r.users(),
|
||||
r.version(defaultVersionInfo),
|
||||
r.organizations(),
|
||||
|
||||
// Workspace Commands
|
||||
r.autoupdate(),
|
||||
|
@ -698,14 +699,44 @@ func (r *RootCmd) createAgentClient() (*agentsdk.Client, error) {
|
|||
}
|
||||
|
||||
// CurrentOrganization returns the currently active organization for the authenticated user.
|
||||
func CurrentOrganization(inv *clibase.Invocation, client *codersdk.Client) (codersdk.Organization, error) {
|
||||
func CurrentOrganization(r *RootCmd, inv *clibase.Invocation, client *codersdk.Client) (codersdk.Organization, error) {
|
||||
conf := r.createConfig()
|
||||
selected := ""
|
||||
if conf.Organization().Exists() {
|
||||
org, err := conf.Organization().Read()
|
||||
if err != nil {
|
||||
return codersdk.Organization{}, fmt.Errorf("read selected organization from config file %q: %w", conf.Organization(), err)
|
||||
}
|
||||
selected = org
|
||||
}
|
||||
|
||||
// Verify the org exists and the user is a member
|
||||
orgs, err := client.OrganizationsByUser(inv.Context(), codersdk.Me)
|
||||
if err != nil {
|
||||
return codersdk.Organization{}, nil
|
||||
return codersdk.Organization{}, err
|
||||
}
|
||||
// For now, we won't use the config to set this.
|
||||
// Eventually, we will support changing using "coder switch <org>"
|
||||
return orgs[0], nil
|
||||
|
||||
// User manually selected an organization
|
||||
if selected != "" {
|
||||
index := slices.IndexFunc(orgs, func(org codersdk.Organization) bool {
|
||||
return org.Name == selected || org.ID.String() == selected
|
||||
})
|
||||
|
||||
if index < 0 {
|
||||
return codersdk.Organization{}, xerrors.Errorf("organization %q not found, are you sure you are a member of this organization?", selected)
|
||||
}
|
||||
return orgs[index], nil
|
||||
}
|
||||
|
||||
// User did not select an organization, so use the default.
|
||||
index := slices.IndexFunc(orgs, func(org codersdk.Organization) bool {
|
||||
return org.IsDefault
|
||||
})
|
||||
if index < 0 {
|
||||
return codersdk.Organization{}, xerrors.Errorf("unable to determine current organization. Use 'coder organizations switch <org>' to select an organization to use")
|
||||
}
|
||||
|
||||
return orgs[index], nil
|
||||
}
|
||||
|
||||
func splitNamedWorkspace(identifier string) (owner string, workspaceName string, err error) {
|
||||
|
|
|
@ -69,7 +69,7 @@ func (r *RootCmd) templateCreate() *clibase.Cmd {
|
|||
}
|
||||
}
|
||||
|
||||
organization, err := CurrentOrganization(inv, client)
|
||||
organization, err := CurrentOrganization(r, inv, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -243,19 +243,7 @@ func TestTemplateCreate(t *testing.T) {
|
|||
assert.Error(t, err)
|
||||
}()
|
||||
|
||||
matches := []struct {
|
||||
match string
|
||||
write string
|
||||
}{
|
||||
{match: "Upload", write: "yes"},
|
||||
}
|
||||
for _, m := range matches {
|
||||
pty.ExpectMatch(m.match)
|
||||
if len(m.write) > 0 {
|
||||
pty.WriteLine(m.write)
|
||||
}
|
||||
}
|
||||
|
||||
pty.ExpectMatch("context canceled")
|
||||
<-ctx.Done()
|
||||
})
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ func (r *RootCmd) templateDelete() *clibase.Cmd {
|
|||
templates = []codersdk.Template{}
|
||||
)
|
||||
|
||||
organization, err := CurrentOrganization(inv, client)
|
||||
organization, err := CurrentOrganization(r, inv, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@ func (r *RootCmd) templateEdit() *clibase.Cmd {
|
|||
}
|
||||
}
|
||||
|
||||
organization, err := CurrentOrganization(inv, client)
|
||||
organization, err := CurrentOrganization(r, inv, client)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("get current organization: %w", err)
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ func (r *RootCmd) templateList() *clibase.Cmd {
|
|||
r.InitClient(client),
|
||||
),
|
||||
Handler: func(inv *clibase.Invocation) error {
|
||||
organization, err := CurrentOrganization(inv, client)
|
||||
organization, err := CurrentOrganization(r, inv, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ func (r *RootCmd) templatePull() *clibase.Cmd {
|
|||
return xerrors.Errorf("either tar or zip can be selected")
|
||||
}
|
||||
|
||||
organization, err := CurrentOrganization(inv, client)
|
||||
organization, err := CurrentOrganization(r, inv, client)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("get current organization: %w", err)
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ func (r *RootCmd) templatePush() *clibase.Cmd {
|
|||
Handler: func(inv *clibase.Invocation) error {
|
||||
uploadFlags.setWorkdir(workdir)
|
||||
|
||||
organization, err := CurrentOrganization(inv, client)
|
||||
organization, err := CurrentOrganization(r, inv, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ func (r *RootCmd) setArchiveTemplateVersion(archive bool) *clibase.Cmd {
|
|||
versions []codersdk.TemplateVersion
|
||||
)
|
||||
|
||||
organization, err := CurrentOrganization(inv, client)
|
||||
organization, err := CurrentOrganization(r, inv, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ func (r *RootCmd) archiveTemplateVersions() *clibase.Cmd {
|
|||
templates = []codersdk.Template{}
|
||||
)
|
||||
|
||||
organization, err := CurrentOrganization(inv, client)
|
||||
organization, err := CurrentOrganization(r, inv, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -93,7 +93,7 @@ func (r *RootCmd) templateVersionsList() *clibase.Cmd {
|
|||
},
|
||||
},
|
||||
Handler: func(inv *clibase.Invocation) error {
|
||||
organization, err := CurrentOrganization(inv, client)
|
||||
organization, err := CurrentOrganization(r, inv, client)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("get current organization: %w", err)
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ func (r *RootCmd) userCreate() *clibase.Cmd {
|
|||
r.InitClient(client),
|
||||
),
|
||||
Handler: func(inv *clibase.Invocation) error {
|
||||
organization, err := CurrentOrganization(inv, client)
|
||||
organization, err := CurrentOrganization(r, inv, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -2,8 +2,12 @@ package httpmw
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
||||
"github.com/coder/coder/v2/coderd/httpapi"
|
||||
|
@ -40,19 +44,34 @@ func ExtractOrganizationParam(db database.Store) func(http.Handler) http.Handler
|
|||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
orgID, ok := ParseUUIDParam(rw, r, "organization")
|
||||
if !ok {
|
||||
arg := chi.URLParam(r, "organization")
|
||||
if arg == "" {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "\"organization\" must be provided.",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
organization, err := db.GetOrganizationByID(ctx, orgID)
|
||||
var organization database.Organization
|
||||
var err error
|
||||
// Try by name or uuid.
|
||||
id, err := uuid.Parse(arg)
|
||||
if err == nil {
|
||||
organization, err = db.GetOrganizationByID(ctx, id)
|
||||
} else {
|
||||
organization, err = db.GetOrganizationByName(ctx, arg)
|
||||
}
|
||||
if httpapi.Is404Error(err) {
|
||||
httpapi.ResourceNotFound(rw)
|
||||
httpapi.Write(ctx, rw, http.StatusNotFound, codersdk.Response{
|
||||
Message: fmt.Sprintf("Organization %q not found.", arg),
|
||||
Detail: "Provide either the organization id or name.",
|
||||
})
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error fetching organization.",
|
||||
Message: fmt.Sprintf("Internal error fetching organization %q.", arg),
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
|
|
|
@ -103,7 +103,7 @@ func TestOrganizationParam(t *testing.T) {
|
|||
rtr.ServeHTTP(rw, r)
|
||||
res := rw.Result()
|
||||
defer res.Body.Close()
|
||||
require.Equal(t, http.StatusBadRequest, res.StatusCode)
|
||||
require.Equal(t, http.StatusNotFound, res.StatusCode)
|
||||
})
|
||||
|
||||
t.Run("NotInOrganization", func(t *testing.T) {
|
||||
|
@ -160,8 +160,6 @@ func TestOrganizationParam(t *testing.T) {
|
|||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
chi.RouteContext(r.Context()).URLParams.Add("organization", organization.ID.String())
|
||||
chi.RouteContext(r.Context()).URLParams.Add("user", user.ID.String())
|
||||
rtr.Use(
|
||||
httpmw.ExtractAPIKeyMW(httpmw.ExtractAPIKeyConfig{
|
||||
DB: db,
|
||||
|
@ -194,9 +192,21 @@ func TestOrganizationParam(t *testing.T) {
|
|||
assert.NotEmpty(t, orgMem.OrganizationMember.UserID)
|
||||
assert.NotEmpty(t, orgMem.OrganizationMember.Roles)
|
||||
})
|
||||
|
||||
// Try by ID
|
||||
chi.RouteContext(r.Context()).URLParams.Add("organization", organization.ID.String())
|
||||
chi.RouteContext(r.Context()).URLParams.Add("user", user.ID.String())
|
||||
rtr.ServeHTTP(rw, r)
|
||||
res := rw.Result()
|
||||
defer res.Body.Close()
|
||||
require.Equal(t, http.StatusOK, res.StatusCode)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, "by id")
|
||||
|
||||
// Try by name
|
||||
chi.RouteContext(r.Context()).URLParams.Add("organization", organization.Name)
|
||||
chi.RouteContext(r.Context()).URLParams.Add("user", user.ID.String())
|
||||
rtr.ServeHTTP(rw, r)
|
||||
res = rw.Result()
|
||||
defer res.Body.Close()
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, "by name")
|
||||
})
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ func TestOrganizationByUserAndName(t *testing.T) {
|
|||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
|
||||
_, err := client.OrganizationByName(ctx, codersdk.Me, "nothing")
|
||||
_, err := client.OrganizationByUserAndName(ctx, codersdk.Me, "nothing")
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, http.StatusNotFound, apiErr.StatusCode())
|
||||
|
@ -85,7 +85,7 @@ func TestOrganizationByUserAndName(t *testing.T) {
|
|||
Name: "another",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = other.OrganizationByName(ctx, codersdk.Me, org.Name)
|
||||
_, err = other.OrganizationByUserAndName(ctx, codersdk.Me, org.Name)
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, http.StatusNotFound, apiErr.StatusCode())
|
||||
|
@ -101,7 +101,7 @@ func TestOrganizationByUserAndName(t *testing.T) {
|
|||
|
||||
org, err := client.Organization(ctx, user.OrganizationID)
|
||||
require.NoError(t, err)
|
||||
_, err = client.OrganizationByName(ctx, codersdk.Me, org.Name)
|
||||
_, err = client.OrganizationByUserAndName(ctx, codersdk.Me, org.Name)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -26,11 +26,11 @@ const (
|
|||
|
||||
// Organization is the JSON representation of a Coder organization.
|
||||
type Organization struct {
|
||||
ID uuid.UUID `json:"id" validate:"required" format:"uuid"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
CreatedAt time.Time `json:"created_at" validate:"required" format:"date-time"`
|
||||
UpdatedAt time.Time `json:"updated_at" validate:"required" format:"date-time"`
|
||||
IsDefault bool `json:"is_default" validate:"required"`
|
||||
ID uuid.UUID `table:"id" json:"id" validate:"required" format:"uuid"`
|
||||
Name string `table:"name,default_sort" json:"name" validate:"required"`
|
||||
CreatedAt time.Time `table:"created_at" json:"created_at" validate:"required" format:"date-time"`
|
||||
UpdatedAt time.Time `table:"updated_at" json:"updated_at" validate:"required" format:"date-time"`
|
||||
IsDefault bool `table:"default" json:"is_default" validate:"required"`
|
||||
}
|
||||
|
||||
type OrganizationMember struct {
|
||||
|
@ -153,8 +153,8 @@ type CreateWorkspaceRequest struct {
|
|||
AutomaticUpdates AutomaticUpdates `json:"automatic_updates,omitempty"`
|
||||
}
|
||||
|
||||
func (c *Client) Organization(ctx context.Context, id uuid.UUID) (Organization, error) {
|
||||
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/organizations/%s", id.String()), nil)
|
||||
func (c *Client) OrganizationByName(ctx context.Context, name string) (Organization, error) {
|
||||
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/organizations/%s", name), nil)
|
||||
if err != nil {
|
||||
return Organization{}, xerrors.Errorf("execute request: %w", err)
|
||||
}
|
||||
|
@ -168,6 +168,12 @@ func (c *Client) Organization(ctx context.Context, id uuid.UUID) (Organization,
|
|||
return organization, json.NewDecoder(res.Body).Decode(&organization)
|
||||
}
|
||||
|
||||
func (c *Client) Organization(ctx context.Context, id uuid.UUID) (Organization, error) {
|
||||
// OrganizationByName uses the exact same endpoint. It accepts a name or uuid.
|
||||
// We just provide this function for type safety.
|
||||
return c.OrganizationByName(ctx, id.String())
|
||||
}
|
||||
|
||||
// ProvisionerDaemons returns provisioner daemons available.
|
||||
func (c *Client) ProvisionerDaemons(ctx context.Context) ([]ProvisionerDaemon, error) {
|
||||
res, err := c.Request(ctx, http.MethodGet,
|
||||
|
|
|
@ -573,7 +573,7 @@ func (c *Client) OrganizationsByUser(ctx context.Context, user string) ([]Organi
|
|||
return orgs, json.NewDecoder(res.Body).Decode(&orgs)
|
||||
}
|
||||
|
||||
func (c *Client) OrganizationByName(ctx context.Context, user string, name string) (Organization, error) {
|
||||
func (c *Client) OrganizationByUserAndName(ctx context.Context, user string, name string) (Organization, error) {
|
||||
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s/organizations/%s", user, name), nil)
|
||||
if err != nil {
|
||||
return Organization{}, err
|
||||
|
|
|
@ -29,7 +29,7 @@ func (r *RootCmd) groupCreate() *clibase.Cmd {
|
|||
Handler: func(inv *clibase.Invocation) error {
|
||||
ctx := inv.Context()
|
||||
|
||||
org, err := agpl.CurrentOrganization(inv, client)
|
||||
org, err := agpl.CurrentOrganization(&r.RootCmd, inv, client)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("current organization: %w", err)
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ func (r *RootCmd) groupDelete() *clibase.Cmd {
|
|||
groupName = inv.Args[0]
|
||||
)
|
||||
|
||||
org, err := agpl.CurrentOrganization(inv, client)
|
||||
org, err := agpl.CurrentOrganization(&r.RootCmd, inv, client)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("current organization: %w", err)
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ func (r *RootCmd) groupEdit() *clibase.Cmd {
|
|||
groupName = inv.Args[0]
|
||||
)
|
||||
|
||||
org, err := agpl.CurrentOrganization(inv, client)
|
||||
org, err := agpl.CurrentOrganization(&r.RootCmd, inv, client)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("current organization: %w", err)
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ func (r *RootCmd) groupList() *clibase.Cmd {
|
|||
Handler: func(inv *clibase.Invocation) error {
|
||||
ctx := inv.Context()
|
||||
|
||||
org, err := agpl.CurrentOrganization(inv, client)
|
||||
org, err := agpl.CurrentOrganization(&r.RootCmd, inv, client)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("current organization: %w", err)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue