-
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.
Add support for organizations, ref: #113
We had to revert the support of organizations. This commit reverts the following commits: 05fc33c 8de0c69 628d9c8 86cfe30
- Loading branch information
Showing
7 changed files
with
403 additions
and
35 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,72 @@ | ||
package koyeb | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"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 { | ||
errBuf := make([]byte, 1024) | ||
// if the body can't be read, it won't be displayed in the error below | ||
resp.Body.Read(errBuf) // nolint:errcheck | ||
return "", &errors.CLIError{ | ||
What: "Error while switching the current organization", | ||
Why: fmt.Sprintf("the API endpoint which switches the current organization returned an error %d", resp.StatusCode), | ||
Additional: []string{ | ||
"You provided an organization id with the --organization flag, or the `organization` field is set in your configuration file.", | ||
"The value provided is likely incorrect, or you don't have access to this organization.", | ||
}, | ||
Orig: fmt.Errorf("HTTP/%d\n\n%s", resp.StatusCode, errBuf), | ||
Solution: "List your organizations with `koyeb --organization=\"\" organization list`, then switch to the organization you want to use with `koyeb --organization=\"\" organization switch <id>`. Finally you can run your commands again, without the --organization flag.", | ||
} | ||
} | ||
return *res.Token.Id, nil | ||
} |
Oops, something went wrong.