-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create
koyeb orga list
and koyeb orga switch
- Loading branch information
Showing
7 changed files
with
358 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
package idmapper | ||
|
||
import ( | ||
"context" | ||
"strconv" | ||
|
||
"github.com/koyeb/koyeb-api-client-go/api/v1/koyeb" | ||
"github.com/koyeb/koyeb-cli/pkg/koyeb/errors" | ||
) | ||
|
||
type OrganizationMapper struct { | ||
ctx context.Context | ||
client *koyeb.APIClient | ||
fetched bool | ||
sidMap *IDMap | ||
nameMap *IDMap | ||
} | ||
|
||
func NewOrganizationMapper(ctx context.Context, client *koyeb.APIClient) *OrganizationMapper { | ||
return &OrganizationMapper{ | ||
ctx: ctx, | ||
client: client, | ||
fetched: false, | ||
sidMap: NewIDMap(), | ||
nameMap: NewIDMap(), | ||
} | ||
} | ||
|
||
func (mapper *OrganizationMapper) ResolveID(val string) (string, error) { | ||
if IsUUIDv4(val) { | ||
return val, nil | ||
} | ||
|
||
if !mapper.fetched { | ||
err := mapper.fetch() | ||
if err != nil { | ||
return "", err | ||
} | ||
} | ||
|
||
id, ok := mapper.sidMap.GetID(val) | ||
if ok { | ||
return id, nil | ||
} | ||
|
||
id, ok = mapper.nameMap.GetID(val) | ||
if ok { | ||
return id, nil | ||
} | ||
|
||
return "", errors.NewCLIErrorForMapperResolve( | ||
"organization", | ||
val, | ||
[]string{"organization full UUID", "organization short ID (8 characters)", "organization name"}, | ||
) | ||
} | ||
|
||
func (mapper *OrganizationMapper) getCurrentUserId() (string, error) { | ||
res, resp, err := mapper.client.ProfileApi.GetCurrentUser(mapper.ctx).Execute() | ||
if err != nil { | ||
return "", errors.NewCLIErrorFromAPIError("Your authentication token is not linked to a user", err, resp) | ||
} | ||
return *res.GetUser().Id, nil | ||
} | ||
|
||
func (mapper *OrganizationMapper) fetch() error { | ||
radix := NewRadixTree() | ||
|
||
userId, err := mapper.getCurrentUserId() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
page := int64(0) | ||
offset := int64(0) | ||
limit := int64(100) | ||
for { | ||
res, resp, err := mapper.client.OrganizationMembersApi. | ||
ListOrganizationMembers(mapper.ctx). | ||
UserId(userId). | ||
Limit(strconv.FormatInt(limit, 10)). | ||
Offset(strconv.FormatInt(offset, 10)). | ||
Execute() | ||
if err != nil { | ||
return errors.NewCLIErrorFromAPIError( | ||
"Error listing organizations to resolve the provided identifier to an object ID", | ||
err, | ||
resp, | ||
) | ||
} | ||
|
||
members := res.GetMembers() | ||
for i := range members { | ||
member := &members[i] | ||
radix.Insert(getKey(member.Organization.GetId()), member) | ||
} | ||
|
||
page++ | ||
offset = page * limit | ||
if offset >= res.GetCount() { | ||
break | ||
} | ||
} | ||
|
||
minLength := radix.MinimalLength(8) | ||
err = radix.ForEach(func(key Key, value Value) error { | ||
member := value.(*koyeb.OrganizationMember) | ||
id := member.Organization.GetId() | ||
name := member.Organization.GetName() | ||
sid := getShortID(id, minLength) | ||
|
||
mapper.sidMap.Set(id, sid) | ||
mapper.nameMap.Set(id, name) | ||
|
||
return nil | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
mapper.fetched = true | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package koyeb | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/koyeb/koyeb-api-client-go/api/v1/koyeb" | ||
"github.com/koyeb/koyeb-cli/pkg/koyeb/errors" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
func NewOrganizationCmd() *cobra.Command { | ||
h := NewOrganizationHandler() | ||
rootCmd := &cobra.Command{ | ||
Use: "organizations ACTION", | ||
Aliases: []string{"organizations", "organization", "orgas", "orga", "orgs", "org", "organisations", "organisation"}, | ||
Short: "Organization", | ||
} | ||
listCmd := &cobra.Command{ | ||
Use: "list", | ||
Short: "List organizations", | ||
RunE: WithCLIContext(h.List), | ||
} | ||
rootCmd.AddCommand(listCmd) | ||
|
||
switchCmd := &cobra.Command{ | ||
Use: "switch", | ||
Short: "Switch the CLI context to another organization", | ||
RunE: WithCLIContext(h.Switch), | ||
} | ||
rootCmd.AddCommand(switchCmd) | ||
return rootCmd | ||
} | ||
|
||
func NewOrganizationHandler() *OrganizationHandler { | ||
return &OrganizationHandler{} | ||
} | ||
|
||
type OrganizationHandler struct { | ||
} | ||
|
||
func ResolveOrganizationArgs(ctx *CLIContext, val string) (string, error) { | ||
organizationMapper := ctx.Mapper.Organization() | ||
id, err := organizationMapper.ResolveID(val) | ||
if err != nil { | ||
return "", err | ||
} | ||
return id, nil | ||
} | ||
|
||
// GetOrganizationToken calls /v1/organizations/{organizationId}/switch which returns a token to access the resources of organizationId | ||
func GetOrganizationToken(api koyeb.OrganizationApi, ctx context.Context, organizationId string) (string, error) { | ||
//SwitchOrganization requires to pass an empty body | ||
body := make(map[string]interface{}) | ||
res, resp, err := api.SwitchOrganization(ctx, organizationId).Body(body).Execute() | ||
if err != nil { | ||
return "", errors.NewCLIErrorFromAPIError("unable to switch the current organization", err, resp) | ||
} | ||
return *res.Token.Id, nil | ||
} |
Oops, something went wrong.