Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(fctl): add reconciliation #1077

Merged
merged 2 commits into from
Dec 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 154 additions & 0 deletions components/fctl/cmd/reconciliation/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package reconciliation

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 ListStore struct {
Cursor *shared.ReconciliationsCursorResponseCursor `json:"cursor"`
}

type ListController struct {
store *ListStore

cursorFlag string
pageSizeFlag string
}

var _ fctl.Controller[*ListStore] = (*ListController)(nil)

func NewListStore() *ListStore {
return &ListStore{
Cursor: &shared.ReconciliationsCursorResponseCursor{},
}
}

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) {
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.Reconciliation.ListReconciliations(
cmd.Context(),
operations.ListReconciliationsRequest{
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.ReconciliationsCursorResponse.Cursor

return c, nil
}

func (c *ListController) Render(cmd *cobra.Command, args []string) error {
tableData := fctl.Map(c.store.Cursor.Data, func(p shared.Reconciliation) []string {
return []string{
p.ID,
p.PolicyID,
p.CreatedAt.Format(time.RFC3339),
p.ReconciledAtLedger.Format(time.RFC3339),
p.ReconciledAtPayments.Format(time.RFC3339),
p.Status,
}
})
tableData = fctl.Prepend(tableData, []string{"ID", "PolicyID", "CreatedAt", "ReconciledAtLedger",
"ReconciledAtPayments", "Status"})
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 reconciliations"),
fctl.WithStringFlag(c.cursorFlag, "", "Cursor"),
fctl.WithIntFlag(c.pageSizeFlag, 0, "PageSize"),
fctl.WithController[*ListStore](c),
)
}
92 changes: 92 additions & 0 deletions components/fctl/cmd/reconciliation/policies/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package policies

import (
"encoding/json"
"fmt"

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 {
PolicyID string `json:"policyID"`
}
type CreateController struct {
store *CreateStore
}

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 <file>|-",
fctl.WithConfirmFlag(),
fctl.WithShortDescription("Create a policy"),
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) {
soc, err := fctl.GetStackOrganizationConfig(cmd)
if err != nil {
return nil, err
}

if !fctl.CheckStackApprobation(cmd, soc.Stack, "You are about to create a new policy") {
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.PolicyRequest{}
if err := json.Unmarshal([]byte(script), &request); err != nil {
return nil, err
}

//nolint:gosimple
response, err := client.Reconciliation.CreatePolicy(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.PolicyID = response.PolicyResponse.Data.ID

return c, nil
}

func (c *CreateController) Render(cmd *cobra.Command, args []string) error {
pterm.Success.WithWriter(cmd.OutOrStdout()).Printfln("Policy created with ID: %s", c.store.PolicyID)

return nil
}
97 changes: 97 additions & 0 deletions components/fctl/cmd/reconciliation/policies/delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package policies

import (
"fmt"

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 {
PolicyID string `json:"policyID"`
Success bool `json:"success"`
}

type DeleteController struct {
store *DeleteStore
}

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 <policyID>",
fctl.WithConfirmFlag(),
fctl.WithAliases("d"),
fctl.WithShortDescription("Delete a policy"),
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) {
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.Reconciliation.DeletePolicy(
cmd.Context(),
operations.DeletePolicyRequest{
PolicyID: args[0],
},
)

if err != nil {
return nil, err
}

if response.StatusCode >= 300 {
return nil, fmt.Errorf("unexpected status code: %d", response.StatusCode)
}

c.store.PolicyID = 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("Policy %s Deleted!", c.store.PolicyID)
return nil
}
Loading