diff --git a/Earthfile b/Earthfile index 15cf2caa..46fe9f47 100644 --- a/Earthfile +++ b/Earthfile @@ -1,7 +1,7 @@ VERSION 0.8 PROJECT FormanceHQ/fctl -IMPORT github.com/formancehq/earthly:tags/v0.16.0 AS core +IMPORT github.com/formancehq/earthly:tags/v0.19.0 AS core FROM core+base-image diff --git a/cmd/ledger/logs/list.go b/cmd/ledger/logs/list.go new file mode 100644 index 00000000..17bfd84d --- /dev/null +++ b/cmd/ledger/logs/list.go @@ -0,0 +1,142 @@ +package logs + +import ( + "fmt" + "strconv" + "time" + + "github.com/formancehq/fctl/cmd/ledger/internal" + fctl "github.com/formancehq/fctl/pkg" + "github.com/formancehq/formance-sdk-go/v3/pkg/models/operations" + "github.com/formancehq/formance-sdk-go/v3/pkg/models/shared" + "github.com/formancehq/go-libs/pointer" + "github.com/pterm/pterm" + "github.com/spf13/cobra" +) + +var timeRFC = time.RFC3339Nano +var ( + pageSizeFlag = "page-size" + afterFlag = "after" + startTimeFlag = "start-time" + endTimeFLag = "end-time" + cursorFlag = "cursor" +) + +type ListStore struct { + Cursor shared.LogsCursorResponseCursor `json:"cursor"` +} + +type ListController struct { + store *ListStore +} + +var _ fctl.Controller[*ListStore] = (*ListController)(nil) + +func NewDefaultListStore() *ListStore { + return &ListStore{} +} + +func NewListController() *ListController { + return &ListController{ + store: NewDefaultListStore(), + } +} + +func NewListCommand() *cobra.Command { + c := NewListController() + return fctl.NewCommand("list", + fctl.WithAliases("ls", "l"), + fctl.WithShortDescription("List logs"), + fctl.WithArgs(cobra.ExactArgs(0)), + fctl.WithIntFlag(pageSizeFlag, 15, "Page size"), + fctl.WithStringFlag(cursorFlag, "", "Logs Cursor"), + fctl.WithStringFlag(afterFlag, "", "Pagination cursor, will return the logs after a given ID. (in descending order)."), + fctl.WithStringFlag(startTimeFlag, "", fmt.Sprintf("Start time (time.RFC %s)", timeRFC)), + fctl.WithStringFlag(endTimeFLag, "", fmt.Sprintf("End time (time.RFC %s)", timeRFC)), + fctl.WithController(c), + ) +} + +func (c *ListController) GetStore() *ListStore { + return c.store +} + +func (c *ListController) Run(cmd *cobra.Command, args []string) (fctl.Renderable, error) { + + store := fctl.GetStackStore(cmd.Context()) + request := operations.ListLogsRequest{ + Ledger: fctl.GetString(cmd, internal.LedgerFlag), + PageSize: pointer.For(int64(fctl.GetInt(cmd, pageSizeFlag))), + } + + if after := fctl.GetString(cmd, afterFlag); after != "" { + request.After = &after + } + + if startTime := fctl.GetString(cmd, startTimeFlag); startTime != "" { + str, err := time.Parse(timeRFC, startTime) + if err != nil { + return nil, fmt.Errorf(`invalid start time parsing with %s: %w`, timeRFC, err) + } + request.StartTime = &str + } + + if endTime := fctl.GetString(cmd, endTimeFLag); endTime != "" { + str, err := time.Parse(timeRFC, endTime) + if err != nil { + return nil, fmt.Errorf(`invalid end time parsing with %s: %w`, timeRFC, err) + } + request.EndTime = &str + } + + rsp, err := store.Client().Ledger.V1.ListLogs(cmd.Context(), request) + if err != nil { + return nil, err + } + + c.store.Cursor = rsp.LogsCursorResponse.Cursor + + return c, nil +} + +func (c *ListController) Render(cmd *cobra.Command, args []string) error { + fmt.Println("") + 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 + } + + tableData = fctl.Map(c.store.Cursor.Data, func(log shared.Log) []string { + return []string{ + log.Date.Format(timeRFC), + strconv.FormatInt(log.ID, 10), + string(log.Type), + log.Hash, + } + }) + tableData = fctl.Prepend(tableData, []string{"Date", "ID", "Type", "Hash"}) + return pterm.DefaultTable. + WithHasHeader(). + WithWriter(cmd.OutOrStdout()). + WithData(tableData). + Render() +} diff --git a/cmd/ledger/logs/root.go b/cmd/ledger/logs/root.go new file mode 100644 index 00000000..f7c90a3a --- /dev/null +++ b/cmd/ledger/logs/root.go @@ -0,0 +1,16 @@ +package logs + +import ( + fctl "github.com/formancehq/fctl/pkg" + "github.com/spf13/cobra" +) + +func NewLogsCommand() *cobra.Command { + return fctl.NewCommand("logs", + fctl.WithAliases("log", "l"), + fctl.WithShortDescription("Logs management"), + fctl.WithChildCommands( + NewListCommand(), + ), + ) +} diff --git a/cmd/ledger/root.go b/cmd/ledger/root.go index b1a3b7bc..59c71b14 100644 --- a/cmd/ledger/root.go +++ b/cmd/ledger/root.go @@ -3,6 +3,7 @@ package ledger import ( "github.com/formancehq/fctl/cmd/ledger/accounts" "github.com/formancehq/fctl/cmd/ledger/internal" + "github.com/formancehq/fctl/cmd/ledger/logs" "github.com/formancehq/fctl/cmd/ledger/transactions" "github.com/formancehq/fctl/cmd/ledger/volumes" fctl "github.com/formancehq/fctl/pkg" @@ -24,6 +25,7 @@ func NewCommand() *cobra.Command { NewDeleteMetadataCommand(), NewExportCommand(), NewImportCommand(), + logs.NewLogsCommand(), transactions.NewLedgerTransactionsCommand(), accounts.NewLedgerAccountsCommand(), volumes.NewLedgerVolumesCommand(), diff --git a/go.mod b/go.mod index e7134ca7..e545413a 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,6 @@ module github.com/formancehq/fctl -go 1.22.0 - -toolchain go1.22.7 +go 1.23 require ( github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2