Skip to content

Commit

Permalink
feat(fctl): add reconciliation (#1077)
Browse files Browse the repository at this point in the history
  • Loading branch information
paul-nicolas authored Dec 22, 2023
1 parent 5eceba9 commit 562120a
Show file tree
Hide file tree
Showing 21 changed files with 1,039 additions and 49 deletions.
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

0 comments on commit 562120a

Please sign in to comment.