diff --git a/cli/cmd/list.go b/cli/cmd/list.go index f6961781..6620bed0 100644 --- a/cli/cmd/list.go +++ b/cli/cmd/list.go @@ -23,4 +23,5 @@ func AddListCmd(rootCmd *cobra.Command, opts GlobalOptions) { } rootCmd.AddCommand(list.cmd) AddListPackagesCmd(list.cmd, opts) + AddListAPIKeysCmd(list.cmd, opts) } diff --git a/cli/cmd/list_api_keys.go b/cli/cmd/list_api_keys.go new file mode 100644 index 00000000..da0429d8 --- /dev/null +++ b/cli/cmd/list_api_keys.go @@ -0,0 +1,56 @@ +// Copyright (c) Codesphere Inc. +// SPDX-License-Identifier: Apache-2.0 + +package cmd + +import ( + "fmt" + + "github.com/codesphere-cloud/oms/internal/portal" + "github.com/codesphere-cloud/oms/internal/util" + + "github.com/codesphere-cloud/cs-go/pkg/io" + "github.com/jedib0t/go-pretty/v6/table" + "github.com/spf13/cobra" +) + +type ListAPIKeysCmd struct { + cmd *cobra.Command + TableWriter util.TableWriter +} + +func (c *ListAPIKeysCmd) RunE(_ *cobra.Command, args []string) error { + p := portal.NewPortalClient() + keys, err := p.ListAPIKeys() + if err != nil { + return fmt.Errorf("failed to list api keys: %w", err) + } + + c.PrintKeysTable(keys) + return nil +} + +func AddListAPIKeysCmd(list *cobra.Command, opts GlobalOptions) { + c := ListAPIKeysCmd{ + cmd: &cobra.Command{ + Use: "api-keys", + Short: "List API keys", + Long: io.Long(`List API keys registered in the OMS portal.`), + }, + TableWriter: util.GetTableWriter(), + } + + c.cmd.RunE = c.RunE + + list.AddCommand(c.cmd) +} + +func (c *ListAPIKeysCmd) PrintKeysTable(keys []portal.ApiKey) { + c.TableWriter.AppendHeader(table.Row{"ID", "Owner", "Organization", "Role", "Created", "Expires"}) + + for _, k := range keys { + c.TableWriter.AppendRow(table.Row{k.KeyID, k.Owner, k.Organization, k.Role, k.CreatedAt, k.ExpiresAt}) + } + + c.TableWriter.Render() +} diff --git a/internal/portal/api_key.go b/internal/portal/api_key.go index 5b997b4f..0c0648c1 100644 --- a/internal/portal/api_key.go +++ b/internal/portal/api_key.go @@ -3,11 +3,12 @@ package portal import "time" type ApiKey struct { - RID string `json:"rid"` - ApiKey string `json:"api_key"` + KeyID string `json:"key_id"` Owner string `json:"owner"` Organization string `json:"organization"` Role string `json:"role"` CreatedAt time.Time `json:"createdAt"` ExpiresAt time.Time `json:"expiresAt"` + // Temp + ApiKey string `json:"api_key"` } diff --git a/internal/portal/http.go b/internal/portal/http.go index 00e24a5c..f53001b2 100644 --- a/internal/portal/http.go +++ b/internal/portal/http.go @@ -25,6 +25,7 @@ type Portal interface { DownloadBuildArtifact(product Product, build Build, file io.Writer) error RegisterAPIKey(owner string, organization string, role string, expiresAt time.Time) error RevokeAPIKey(key string) error + ListAPIKeys() ([]ApiKey, error) } type PortalClient struct { @@ -256,3 +257,17 @@ func (c *PortalClient) RevokeAPIKey(keyId string) error { return nil } + +func (c *PortalClient) ListAPIKeys() ([]ApiKey, error) { + res, _, err := c.GetBody("/keys") + if err != nil { + return nil, fmt.Errorf("failed to list api keys: %w", err) + } + + var keys []ApiKey + if err := json.Unmarshal(res, &keys); err != nil { + return nil, fmt.Errorf("failed to parse api keys response: %w", err) + } + + return keys, nil +} diff --git a/internal/portal/mocks.go b/internal/portal/mocks.go index 8279e003..4181514a 100644 --- a/internal/portal/mocks.go +++ b/internal/portal/mocks.go @@ -5,10 +5,11 @@ package portal import ( - mock "github.com/stretchr/testify/mock" "io" "net/http" "time" + + mock "github.com/stretchr/testify/mock" ) // NewMockPortal creates a new instance of MockPortal. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. @@ -167,6 +168,59 @@ func (_mock *MockPortal) ListBuilds(product Product) (Builds, error) { return r0, r1 } +// ListAPIKeys provides a mock function for the type MockPortal +func (_mock *MockPortal) ListAPIKeys() ([]ApiKey, error) { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for ListAPIKeys") + } + + var r0 []ApiKey + var r1 error + if returnFunc, ok := ret.Get(0).(func() ([]ApiKey, error)); ok { + return returnFunc() + } + if returnFunc, ok := ret.Get(0).(func() []ApiKey); ok { + r0 = returnFunc() + } else { + r0 = ret.Get(0).([]ApiKey) + } + if returnFunc, ok := ret.Get(1).(func() error); ok { + r1 = returnFunc() + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// MockPortal_ListAPIKeys_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListAPIKeys' +type MockPortal_ListAPIKeys_Call struct { + *mock.Call +} + +// ListAPIKeys is a helper method to define mock.On call +func (_e *MockPortal_Expecter) ListAPIKeys() *MockPortal_ListAPIKeys_Call { + return &MockPortal_ListAPIKeys_Call{Call: _e.mock.On("ListAPIKeys")} +} + +func (_c *MockPortal_ListAPIKeys_Call) Run(run func()) *MockPortal_ListAPIKeys_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockPortal_ListAPIKeys_Call) Return(keys []ApiKey, err error) *MockPortal_ListAPIKeys_Call { + _c.Call.Return(keys, err) + return _c +} + +func (_c *MockPortal_ListAPIKeys_Call) RunAndReturn(run func() ([]ApiKey, error)) *MockPortal_ListAPIKeys_Call { + _c.Call.Return(run) + return _c +} + // MockPortal_ListBuilds_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListBuilds' type MockPortal_ListBuilds_Call struct { *mock.Call