diff --git a/components/fctl/cmd/payments/accounts/balances.go b/components/fctl/cmd/payments/accounts/balances.go index a2291d0b5c..7b43c57f72 100644 --- a/components/fctl/cmd/payments/accounts/balances.go +++ b/components/fctl/cmd/payments/accounts/balances.go @@ -17,6 +17,9 @@ type ListBalancesStore struct { type ListBalancesController struct { store *ListBalancesStore + + cursorFlag string + pageSizeFlag string } var _ fctl.Controller[*ListBalancesStore] = (*ListBalancesController)(nil) @@ -30,6 +33,9 @@ func NewListBalanceStore() *ListBalancesStore { func NewListBalancesController() *ListBalancesController { return &ListBalancesController{ store: NewListBalanceStore(), + + cursorFlag: "cursor", + pageSizeFlag: "page-size", } } @@ -58,9 +64,21 @@ func (c *ListBalancesController) Run(cmd *cobra.Command, args []string) (fctl.Re return nil, err } + var cursor *string + if c := fctl.GetString(cmd, c.cursorFlag); c != "" { + cursor = &c + } + + var pageSize *int64 + if ps := fctl.GetInt(cmd, c.pageSizeFlag); ps > 0 { + pageSize = fctl.Ptr(int64(ps)) + } + response, err := client.Payments.GetAccountBalances( cmd.Context(), operations.GetAccountBalancesRequest{ + Cursor: cursor, + PageSize: pageSize, AccountID: args[0], }, ) @@ -89,17 +107,47 @@ func (c *ListBalancesController) Render(cmd *cobra.Command, args []string) error }) tableData = fctl.Prepend(tableData, []string{"ID", "Asset", "Balance", "CreatedAt", "LastUpdatedAt"}) - return pterm.DefaultTable. + if err := pterm.DefaultTable. WithHasHeader(). WithWriter(cmd.OutOrStdout()). WithData(tableData). - Render() + Render(); err != nil { + return err + } + + tableData = pterm.TableData{} + tableData = append(tableData, []string{pterm.LightCyan("HasMore"), fmt.Sprintf("%v", c.store.Cursor.HasMore)}) + tableData = append(tableData, []string{pterm.LightCyan("PageSize"), fmt.Sprintf("%d", c.store.Cursor.PageSize)}) + tableData = append(tableData, []string{pterm.LightCyan("Next"), func() string { + if c.store.Cursor.Next == nil { + return "" + } + return *c.store.Cursor.Next + }()}) + tableData = append(tableData, []string{pterm.LightCyan("Previous"), func() string { + if c.store.Cursor.Previous == nil { + return "" + } + return *c.store.Cursor.Previous + }()}) + + if err := pterm.DefaultTable. + WithWriter(cmd.OutOrStdout()). + WithData(tableData). + Render(); err != nil { + return err + } + + return nil } func NewListBalanceCommand() *cobra.Command { + c := NewListBalancesController() return fctl.NewCommand("balances ", fctl.WithArgs(cobra.ExactArgs(1)), fctl.WithShortDescription("List accounts balances"), - fctl.WithController[*ListBalancesStore](NewListBalancesController()), + fctl.WithStringFlag(c.cursorFlag, "", "Cursor"), + fctl.WithIntFlag(c.pageSizeFlag, 0, "PageSize"), + fctl.WithController[*ListBalancesStore](c), ) } diff --git a/components/fctl/cmd/payments/bankaccounts/create.go b/components/fctl/cmd/payments/bankaccounts/create.go index c0094c6576..58de8f8eed 100644 --- a/components/fctl/cmd/payments/bankaccounts/create.go +++ b/components/fctl/cmd/payments/bankaccounts/create.go @@ -101,7 +101,7 @@ func (c *CreateController) Run(cmd *cobra.Command, args []string) (fctl.Renderab } func (c *CreateController) Render(cmd *cobra.Command, args []string) error { - pterm.Success.WithWriter(cmd.OutOrStdout()).Printfln("Bank Accounts created with ID: %s", c.store.BankAccountID) + pterm.Success.WithWriter(cmd.OutOrStdout()).Printfln("Bank Account created with ID: %s", c.store.BankAccountID) return nil } diff --git a/components/fctl/cmd/payments/pools/add_accounts.go b/components/fctl/cmd/payments/pools/add_accounts.go new file mode 100644 index 0000000000..b2992450bf --- /dev/null +++ b/components/fctl/cmd/payments/pools/add_accounts.go @@ -0,0 +1,108 @@ +package pools + +import ( + "fmt" + + "github.com/formancehq/fctl/cmd/payments/versions" + fctl "github.com/formancehq/fctl/pkg" + "github.com/formancehq/formance-sdk-go/pkg/models/operations" + "github.com/formancehq/formance-sdk-go/pkg/models/shared" + "github.com/pterm/pterm" + "github.com/spf13/cobra" +) + +type AddAccountStore struct { + PoolID string `json:"poolID"` + AccountID string `json:"accountID"` + Success bool `json:"success"` +} +type AddAccountController struct { + PaymentsVersion versions.Version + + store *AddAccountStore +} + +func (c *AddAccountController) SetVersion(version versions.Version) { + c.PaymentsVersion = version +} + +var _ fctl.Controller[*AddAccountStore] = (*AddAccountController)(nil) + +func NewAddAccountStore() *AddAccountStore { + return &AddAccountStore{} +} + +func NewAddAccountController() *AddAccountController { + return &AddAccountController{ + store: NewAddAccountStore(), + } +} + +func NewAddAccountCommand() *cobra.Command { + c := NewAddAccountController() + return fctl.NewCommand("add-account ", + fctl.WithShortDescription("Add account to pool"), + fctl.WithArgs(cobra.ExactArgs(2)), + fctl.WithAliases("add", "a"), + fctl.WithController[*AddAccountStore](c), + ) +} + +func (c *AddAccountController) GetStore() *AddAccountStore { + return c.store +} + +func (c *AddAccountController) Run(cmd *cobra.Command, args []string) (fctl.Renderable, error) { + if err := versions.GetPaymentsVersion(cmd, args, c); err != nil { + return nil, err + } + + if c.PaymentsVersion < versions.V1 { + return nil, fmt.Errorf("pools are only supported in >= v1.0.0") + } + + cfg, err := fctl.GetConfig(cmd) + if err != nil { + return nil, err + } + + organizationID, err := fctl.ResolveOrganizationID(cmd, cfg) + if err != nil { + return nil, err + } + + stack, err := fctl.ResolveStack(cmd, cfg, organizationID) + if err != nil { + return nil, err + } + + ledgerClient, err := fctl.NewStackClient(cmd, cfg, stack) + if err != nil { + return nil, err + } + + response, err := ledgerClient.Payments.AddAccountToPool(cmd.Context(), operations.AddAccountToPoolRequest{ + PoolID: args[0], + AddAccountToPoolRequest: shared.AddAccountToPoolRequest{ + AccountID: args[1], + }, + }) + if err != nil { + return nil, err + } + + if response.StatusCode >= 300 { + return nil, fmt.Errorf("unexpected status code: %d", response.StatusCode) + } + + c.store.PoolID = args[0] + c.store.AccountID = args[1] + + return c, nil +} + +func (c *AddAccountController) Render(cmd *cobra.Command, args []string) error { + pterm.Success.WithWriter(cmd.OutOrStdout()).Printfln("Successfully added '%s' to '%s'", c.store.AccountID, c.store.PoolID) + + return nil +} diff --git a/components/fctl/cmd/payments/pools/balances.go b/components/fctl/cmd/payments/pools/balances.go new file mode 100644 index 0000000000..e55f18cbb2 --- /dev/null +++ b/components/fctl/cmd/payments/pools/balances.go @@ -0,0 +1,108 @@ +package pools + +import ( + "fmt" + "time" + + fctl "github.com/formancehq/fctl/pkg" + "github.com/formancehq/formance-sdk-go/pkg/models/operations" + "github.com/formancehq/formance-sdk-go/pkg/models/shared" + "github.com/pterm/pterm" + "github.com/spf13/cobra" +) + +type BalancesStore struct { + Balances *shared.PoolBalances `json:"balances"` +} + +type BalancesController struct { + store *BalancesStore +} + +var _ fctl.Controller[*BalancesStore] = (*BalancesController)(nil) + +func NewBalancesStore() *BalancesStore { + return &BalancesStore{ + Balances: &shared.PoolBalances{}, + } +} + +func NewBalancesController() *BalancesController { + return &BalancesController{ + store: NewBalancesStore(), + } +} + +func (c *BalancesController) GetStore() *BalancesStore { + return c.store +} + +func (c *BalancesController) Run(cmd *cobra.Command, args []string) (fctl.Renderable, error) { + cfg, err := fctl.GetConfig(cmd) + if err != nil { + return nil, err + } + + organizationID, err := fctl.ResolveOrganizationID(cmd, cfg) + if err != nil { + return nil, err + } + + stack, err := fctl.ResolveStack(cmd, cfg, organizationID) + if err != nil { + return nil, err + } + + client, err := fctl.NewStackClient(cmd, cfg, stack) + if err != nil { + return nil, err + } + + at, err := time.Parse(time.RFC3339, args[1]) + if err != nil { + return nil, err + } + + response, err := client.Payments.GetPoolBalances( + cmd.Context(), + operations.GetPoolBalancesRequest{ + At: at, + PoolID: args[0], + }, + ) + if err != nil { + return nil, err + } + + if response.StatusCode >= 300 { + return nil, fmt.Errorf("unexpected status code: %d", response.StatusCode) + } + + c.store.Balances = &response.PoolBalancesResponse.Data + + return c, nil +} + +func (c *BalancesController) Render(cmd *cobra.Command, args []string) error { + tableData := fctl.Map(c.store.Balances.Balances, func(balance shared.PoolBalance) []string { + return []string{ + balance.Asset, + balance.Amount.String(), + } + }) + tableData = fctl.Prepend(tableData, []string{"Asset", "Amount"}) + return pterm.DefaultTable. + WithHasHeader(). + WithWriter(cmd.OutOrStdout()). + WithData(tableData). + Render() +} + +func NewBalancesCommand() *cobra.Command { + c := NewBalancesController() + return fctl.NewCommand("balances ", + fctl.WithArgs(cobra.ExactArgs(2)), + fctl.WithShortDescription("List pool balances"), + fctl.WithController[*BalancesStore](c), + ) +} diff --git a/components/fctl/cmd/payments/pools/create.go b/components/fctl/cmd/payments/pools/create.go new file mode 100644 index 0000000000..5f52d5a430 --- /dev/null +++ b/components/fctl/cmd/payments/pools/create.go @@ -0,0 +1,107 @@ +package pools + +import ( + "encoding/json" + "fmt" + + "github.com/formancehq/fctl/cmd/payments/versions" + fctl "github.com/formancehq/fctl/pkg" + "github.com/formancehq/formance-sdk-go/pkg/models/shared" + "github.com/pkg/errors" + "github.com/pterm/pterm" + "github.com/spf13/cobra" +) + +type CreateStore struct { + PoolID string `json:"poolID"` +} +type CreateController struct { + PaymentsVersion versions.Version + + store *CreateStore +} + +func (c *CreateController) SetVersion(version versions.Version) { + c.PaymentsVersion = version +} + +var _ fctl.Controller[*CreateStore] = (*CreateController)(nil) + +func NewCreateStore() *CreateStore { + return &CreateStore{} +} + +func NewCreateController() *CreateController { + return &CreateController{ + store: NewCreateStore(), + } +} + +func NewCreateCommand() *cobra.Command { + c := NewCreateController() + return fctl.NewCommand("create |-", + fctl.WithConfirmFlag(), + fctl.WithShortDescription("Create a pool"), + fctl.WithAliases("cr", "c"), + fctl.WithArgs(cobra.ExactArgs(1)), + fctl.WithController[*CreateStore](c), + ) +} + +func (c *CreateController) GetStore() *CreateStore { + return c.store +} + +func (c *CreateController) Run(cmd *cobra.Command, args []string) (fctl.Renderable, error) { + if err := versions.GetPaymentsVersion(cmd, args, c); err != nil { + return nil, err + } + + if c.PaymentsVersion < versions.V1 { + return nil, fmt.Errorf("pools are only supported in >= v1.0.0") + } + + soc, err := fctl.GetStackOrganizationConfig(cmd) + if err != nil { + return nil, err + } + + if !fctl.CheckStackApprobation(cmd, soc.Stack, "You are about to create a new pool") { + return nil, fctl.ErrMissingApproval + } + + client, err := fctl.NewStackClient(cmd, soc.Config, soc.Stack) + if err != nil { + return nil, errors.Wrap(err, "creating stack client") + } + + script, err := fctl.ReadFile(cmd, soc.Stack, args[0]) + if err != nil { + return nil, err + } + + request := shared.PoolRequest{} + if err := json.Unmarshal([]byte(script), &request); err != nil { + return nil, err + } + + //nolint:gosimple + response, err := client.Payments.CreatePool(cmd.Context(), request) + if err != nil { + return nil, err + } + + if response.StatusCode >= 300 { + return nil, fmt.Errorf("unexpected status code: %d", response.StatusCode) + } + + c.store.PoolID = response.PoolResponse.Data.ID + + return c, nil +} + +func (c *CreateController) Render(cmd *cobra.Command, args []string) error { + pterm.Success.WithWriter(cmd.OutOrStdout()).Printfln("Pool created with ID: %s", c.store.PoolID) + + return nil +} diff --git a/components/fctl/cmd/payments/pools/delete.go b/components/fctl/cmd/payments/pools/delete.go new file mode 100644 index 0000000000..e9e6b0cd36 --- /dev/null +++ b/components/fctl/cmd/payments/pools/delete.go @@ -0,0 +1,112 @@ +package pools + +import ( + "fmt" + + "github.com/formancehq/fctl/cmd/payments/versions" + fctl "github.com/formancehq/fctl/pkg" + "github.com/formancehq/formance-sdk-go/pkg/models/operations" + "github.com/pkg/errors" + "github.com/pterm/pterm" + "github.com/spf13/cobra" +) + +type DeleteStore struct { + PoolID string `json:"poolID"` + Success bool `json:"success"` +} + +type DeleteController struct { + PaymentsVersion versions.Version + + store *DeleteStore +} + +func (c *DeleteController) SetVersion(version versions.Version) { + c.PaymentsVersion = version +} + +var _ fctl.Controller[*DeleteStore] = (*DeleteController)(nil) + +func NewDeleteStore() *DeleteStore { + return &DeleteStore{} +} + +func NewDeleteController() *DeleteController { + return &DeleteController{ + store: NewDeleteStore(), + } +} +func NewDeleteCommand() *cobra.Command { + c := NewDeleteController() + return fctl.NewCommand("delete ", + fctl.WithConfirmFlag(), + fctl.WithAliases("d"), + fctl.WithShortDescription("Delete a pool"), + fctl.WithArgs(cobra.ExactArgs(1)), + fctl.WithController[*DeleteStore](c), + ) +} + +func (c *DeleteController) GetStore() *DeleteStore { + return c.store +} + +func (c *DeleteController) Run(cmd *cobra.Command, args []string) (fctl.Renderable, error) { + if err := versions.GetPaymentsVersion(cmd, args, c); err != nil { + return nil, err + } + + if c.PaymentsVersion < versions.V1 { + return nil, fmt.Errorf("pools are only supported in >= v1.0.0") + } + + cfg, err := fctl.GetConfig(cmd) + if err != nil { + return nil, errors.Wrap(err, "retrieving config") + } + + organizationID, err := fctl.ResolveOrganizationID(cmd, cfg) + if err != nil { + return nil, err + } + + stack, err := fctl.ResolveStack(cmd, cfg, organizationID) + if err != nil { + return nil, err + } + + if !fctl.CheckStackApprobation(cmd, stack, "You are about to delete '%s'", args[0]) { + return nil, fctl.ErrMissingApproval + } + + client, err := fctl.NewStackClient(cmd, cfg, stack) + if err != nil { + return nil, errors.Wrap(err, "creating stack client") + } + + response, err := client.Payments.DeletePool( + cmd.Context(), + operations.DeletePoolRequest{ + PoolID: args[0], + }, + ) + + if err != nil { + return nil, err + } + + if response.StatusCode >= 300 { + return nil, fmt.Errorf("unexpected status code: %d", response.StatusCode) + } + + c.store.PoolID = args[0] + c.store.Success = true + + return c, nil +} + +func (c *DeleteController) Render(cmd *cobra.Command, args []string) error { + pterm.Success.WithWriter(cmd.OutOrStdout()).Printfln("Pool %s Deleted!", c.store.PoolID) + return nil +} diff --git a/components/fctl/cmd/payments/pools/list.go b/components/fctl/cmd/payments/pools/list.go new file mode 100644 index 0000000000..f653691178 --- /dev/null +++ b/components/fctl/cmd/payments/pools/list.go @@ -0,0 +1,167 @@ +package pools + +import ( + "fmt" + "strings" + + "github.com/formancehq/fctl/cmd/payments/versions" + fctl "github.com/formancehq/fctl/pkg" + "github.com/formancehq/formance-sdk-go/pkg/models/operations" + "github.com/formancehq/formance-sdk-go/pkg/models/shared" + "github.com/pterm/pterm" + "github.com/spf13/cobra" +) + +type ListStore struct { + Cursor *shared.PoolsCursorCursor `json:"cursor"` +} + +type ListController struct { + PaymentsVersion versions.Version + + store *ListStore + + cursorFlag string + pageSizeFlag string +} + +func (c *ListController) SetVersion(version versions.Version) { + c.PaymentsVersion = version +} + +var _ fctl.Controller[*ListStore] = (*ListController)(nil) + +func NewListStore() *ListStore { + return &ListStore{ + Cursor: &shared.PoolsCursorCursor{}, + } +} + +func NewListController() *ListController { + return &ListController{ + store: NewListStore(), + + cursorFlag: "cursor", + pageSizeFlag: "page-size", + } +} + +func (c *ListController) GetStore() *ListStore { + return c.store +} + +func (c *ListController) Run(cmd *cobra.Command, args []string) (fctl.Renderable, error) { + if err := versions.GetPaymentsVersion(cmd, args, c); err != nil { + return nil, err + } + + if c.PaymentsVersion < versions.V1 { + return nil, fmt.Errorf("pools are only supported in >= v1.0.0") + } + + cfg, err := fctl.GetConfig(cmd) + if err != nil { + return nil, err + } + + organizationID, err := fctl.ResolveOrganizationID(cmd, cfg) + if err != nil { + return nil, err + } + + stack, err := fctl.ResolveStack(cmd, cfg, organizationID) + if err != nil { + return nil, err + } + + client, err := fctl.NewStackClient(cmd, cfg, stack) + if err != nil { + return nil, err + } + + var cursor *string + if c := fctl.GetString(cmd, c.cursorFlag); c != "" { + cursor = &c + } + + var pageSize *int64 + if ps := fctl.GetInt(cmd, c.pageSizeFlag); ps > 0 { + pageSize = fctl.Ptr(int64(ps)) + } + + response, err := client.Payments.ListPools( + cmd.Context(), + operations.ListPoolsRequest{ + Cursor: cursor, + PageSize: pageSize, + }, + ) + if err != nil { + return nil, err + } + + if response.StatusCode >= 300 { + return nil, fmt.Errorf("unexpected status code: %d", response.StatusCode) + } + + c.store.Cursor = &response.PoolsCursor.Cursor + + return c, nil +} + +func (c *ListController) Render(cmd *cobra.Command, args []string) error { + tableData := fctl.Map(c.store.Cursor.Data, func(bc shared.Pool) []string { + return []string{ + bc.ID, + bc.Name, + func() string { + return strings.Join(bc.Accounts, ", ") + }(), + } + }) + tableData = fctl.Prepend(tableData, []string{"ID", "Name", "Accounts"}) + if err := pterm.DefaultTable. + WithHasHeader(). + WithWriter(cmd.OutOrStdout()). + WithData(tableData). + Render(); err != nil { + return err + } + + tableData = pterm.TableData{} + tableData = append(tableData, []string{pterm.LightCyan("HasMore"), fmt.Sprintf("%v", c.store.Cursor.HasMore)}) + tableData = append(tableData, []string{pterm.LightCyan("PageSize"), fmt.Sprintf("%d", c.store.Cursor.PageSize)}) + tableData = append(tableData, []string{pterm.LightCyan("Next"), func() string { + if c.store.Cursor.Next == nil { + return "" + } + return *c.store.Cursor.Next + }()}) + tableData = append(tableData, []string{pterm.LightCyan("Previous"), func() string { + if c.store.Cursor.Previous == nil { + return "" + } + return *c.store.Cursor.Previous + }()}) + + if err := pterm.DefaultTable. + WithWriter(cmd.OutOrStdout()). + WithData(tableData). + Render(); err != nil { + return err + } + + return nil +} + +func NewListCommand() *cobra.Command { + c := NewListController() + return fctl.NewCommand("list", + fctl.WithAliases("ls", "l"), + fctl.WithArgs(cobra.ExactArgs(0)), + fctl.WithShortDescription("List pools"), + fctl.WithStringFlag(c.cursorFlag, "", "Cursor"), + fctl.WithIntFlag(c.pageSizeFlag, 0, "PageSize"), + fctl.WithController[*ListStore](c), + ) +} diff --git a/components/fctl/cmd/payments/pools/remove_account.go b/components/fctl/cmd/payments/pools/remove_account.go new file mode 100644 index 0000000000..fe4e942f5d --- /dev/null +++ b/components/fctl/cmd/payments/pools/remove_account.go @@ -0,0 +1,105 @@ +package pools + +import ( + "fmt" + + "github.com/formancehq/fctl/cmd/payments/versions" + fctl "github.com/formancehq/fctl/pkg" + "github.com/formancehq/formance-sdk-go/pkg/models/operations" + "github.com/pterm/pterm" + "github.com/spf13/cobra" +) + +type RemoveAccountStore struct { + PoolID string `json:"poolID"` + AccountID string `json:"accountID"` + Success bool `json:"success"` +} +type RemoveAccountController struct { + PaymentsVersion versions.Version + + store *RemoveAccountStore +} + +func (c *RemoveAccountController) SetVersion(version versions.Version) { + c.PaymentsVersion = version +} + +var _ fctl.Controller[*RemoveAccountStore] = (*RemoveAccountController)(nil) + +func NewRemoveAccountStore() *RemoveAccountStore { + return &RemoveAccountStore{} +} + +func NewRemoveAccountController() *RemoveAccountController { + return &RemoveAccountController{ + store: NewRemoveAccountStore(), + } +} + +func NewRemoveAccountCommand() *cobra.Command { + c := NewRemoveAccountController() + return fctl.NewCommand("remove-account ", + fctl.WithShortDescription("Remove account from pool"), + fctl.WithArgs(cobra.ExactArgs(2)), + fctl.WithAliases("add", "a"), + fctl.WithController[*RemoveAccountStore](c), + ) +} + +func (c *RemoveAccountController) GetStore() *RemoveAccountStore { + return c.store +} + +func (c *RemoveAccountController) Run(cmd *cobra.Command, args []string) (fctl.Renderable, error) { + if err := versions.GetPaymentsVersion(cmd, args, c); err != nil { + return nil, err + } + + if c.PaymentsVersion < versions.V1 { + return nil, fmt.Errorf("pools are only supported in >= v1.0.0") + } + + cfg, err := fctl.GetConfig(cmd) + if err != nil { + return nil, err + } + + organizationID, err := fctl.ResolveOrganizationID(cmd, cfg) + if err != nil { + return nil, err + } + + stack, err := fctl.ResolveStack(cmd, cfg, organizationID) + if err != nil { + return nil, err + } + + ledgerClient, err := fctl.NewStackClient(cmd, cfg, stack) + if err != nil { + return nil, err + } + + response, err := ledgerClient.Payments.RemoveAccountFromPool(cmd.Context(), operations.RemoveAccountFromPoolRequest{ + PoolID: args[0], + AccountID: args[1], + }) + if err != nil { + return nil, err + } + + if response.StatusCode >= 300 { + return nil, fmt.Errorf("unexpected status code: %d", response.StatusCode) + } + + c.store.PoolID = args[0] + c.store.AccountID = args[1] + + return c, nil +} + +func (c *RemoveAccountController) Render(cmd *cobra.Command, args []string) error { + pterm.Success.WithWriter(cmd.OutOrStdout()).Printfln("Successfully removed '%s' to '%s'", c.store.AccountID, c.store.PoolID) + + return nil +} diff --git a/components/fctl/cmd/payments/pools/root.go b/components/fctl/cmd/payments/pools/root.go index 058ebec649..3fb7e489fa 100644 --- a/components/fctl/cmd/payments/pools/root.go +++ b/components/fctl/cmd/payments/pools/root.go @@ -1 +1,22 @@ package pools + +import ( + fctl "github.com/formancehq/fctl/pkg" + "github.com/spf13/cobra" +) + +func NewPoolsCommand() *cobra.Command { + return fctl.NewCommand("pools", + fctl.WithAliases("p"), + fctl.WithShortDescription("Pools management"), + fctl.WithChildCommands( + NewListCommand(), + NewCreateCommand(), + NewShowCommand(), + NewDeleteCommand(), + NewBalancesCommand(), + NewAddAccountCommand(), + NewRemoveAccountCommand(), + ), + ) +} diff --git a/components/fctl/cmd/payments/pools/show.go b/components/fctl/cmd/payments/pools/show.go new file mode 100644 index 0000000000..4e4d88c38e --- /dev/null +++ b/components/fctl/cmd/payments/pools/show.go @@ -0,0 +1,116 @@ +package pools + +import ( + "fmt" + "strings" + + "github.com/formancehq/fctl/cmd/payments/versions" + fctl "github.com/formancehq/fctl/pkg" + "github.com/formancehq/formance-sdk-go/pkg/models/operations" + "github.com/formancehq/formance-sdk-go/pkg/models/shared" + "github.com/pterm/pterm" + "github.com/spf13/cobra" +) + +type ShowStore struct { + Pool *shared.Pool `json:"pool"` +} +type ShowController struct { + PaymentsVersion versions.Version + + store *ShowStore +} + +func (c *ShowController) SetVersion(version versions.Version) { + c.PaymentsVersion = version +} + +var _ fctl.Controller[*ShowStore] = (*ShowController)(nil) + +func NewShowStore() *ShowStore { + return &ShowStore{} +} + +func NewShowController() *ShowController { + return &ShowController{ + store: NewShowStore(), + } +} + +func NewShowCommand() *cobra.Command { + c := NewShowController() + return fctl.NewCommand("get ", + fctl.WithShortDescription("Get pool"), + fctl.WithArgs(cobra.ExactArgs(1)), + fctl.WithAliases("sh", "s"), + fctl.WithController[*ShowStore](c), + ) +} + +func (c *ShowController) GetStore() *ShowStore { + return c.store +} + +func (c *ShowController) Run(cmd *cobra.Command, args []string) (fctl.Renderable, error) { + if err := versions.GetPaymentsVersion(cmd, args, c); err != nil { + return nil, err + } + + if c.PaymentsVersion < versions.V1 { + return nil, fmt.Errorf("pools are only supported in >= v1.0.0") + } + + cfg, err := fctl.GetConfig(cmd) + if err != nil { + return nil, err + } + + organizationID, err := fctl.ResolveOrganizationID(cmd, cfg) + if err != nil { + return nil, err + } + + stack, err := fctl.ResolveStack(cmd, cfg, organizationID) + if err != nil { + return nil, err + } + + ledgerClient, err := fctl.NewStackClient(cmd, cfg, stack) + if err != nil { + return nil, err + } + + response, err := ledgerClient.Payments.GetPool(cmd.Context(), operations.GetPoolRequest{ + PoolID: args[0], + }) + if err != nil { + return nil, err + } + + if response.StatusCode >= 300 { + return nil, fmt.Errorf("unexpected status code: %d", response.StatusCode) + } + + c.store.Pool = &response.PoolResponse.Data + + return c, nil +} + +func (c *ShowController) Render(cmd *cobra.Command, args []string) error { + fctl.Section.WithWriter(cmd.OutOrStdout()).Println("Information") + tableData := pterm.TableData{} + tableData = append(tableData, []string{pterm.LightCyan("ID"), c.store.Pool.ID}) + tableData = append(tableData, []string{pterm.LightCyan("Name"), c.store.Pool.Name}) + tableData = append(tableData, []string{pterm.LightCyan("Accounts"), func() string { + return strings.Join(c.store.Pool.Accounts, ", ") + }()}) + + if err := pterm.DefaultTable. + WithWriter(cmd.OutOrStdout()). + WithData(tableData). + Render(); err != nil { + return err + } + + return nil +} diff --git a/components/fctl/cmd/payments/root.go b/components/fctl/cmd/payments/root.go index f40fefa531..93384fcf51 100644 --- a/components/fctl/cmd/payments/root.go +++ b/components/fctl/cmd/payments/root.go @@ -5,6 +5,7 @@ import ( "github.com/formancehq/fctl/cmd/payments/bankaccounts" "github.com/formancehq/fctl/cmd/payments/connectors" "github.com/formancehq/fctl/cmd/payments/payments" + "github.com/formancehq/fctl/cmd/payments/pools" "github.com/formancehq/fctl/cmd/payments/transferinitiation" fctl "github.com/formancehq/fctl/pkg" "github.com/spf13/cobra" @@ -19,6 +20,7 @@ func NewCommand() *cobra.Command { transferinitiation.NewTransferInitiationCommand(), bankaccounts.NewBankAccountsCommand(), accounts.NewAccountsCommand(), + pools.NewPoolsCommand(), ), ) }