diff --git a/cmd/payments/connectors/root.go b/cmd/payments/connectors/root.go index 963b6635..948ca218 100644 --- a/cmd/payments/connectors/root.go +++ b/cmd/payments/connectors/root.go @@ -5,6 +5,7 @@ import ( "github.com/formancehq/fctl/v3/cmd/payments/connectors/configs" "github.com/formancehq/fctl/v3/cmd/payments/connectors/install" + "github.com/formancehq/fctl/v3/cmd/payments/connectors/schedules" fctl "github.com/formancehq/fctl/v3/pkg" ) @@ -19,6 +20,7 @@ func NewConnectorsCommand() *cobra.Command { configs.NewUpdateConfigCommand(), configs.NewGetConfigCommand(), NewConnectorListAvailableCommand(), + schedules.NewSchedulesCommand(), ), ) } diff --git a/cmd/payments/connectors/schedules/instances/list.go b/cmd/payments/connectors/schedules/instances/list.go new file mode 100644 index 00000000..f56ad69d --- /dev/null +++ b/cmd/payments/connectors/schedules/instances/list.go @@ -0,0 +1,156 @@ +package instances + +import ( + "fmt" + "time" + + "github.com/pterm/pterm" + "github.com/spf13/cobra" + + "github.com/formancehq/formance-sdk-go/v4/pkg/models/operations" + "github.com/formancehq/formance-sdk-go/v4/pkg/models/payments" + + "github.com/formancehq/fctl/v3/cmd/payments/versions" + fctl "github.com/formancehq/fctl/v3/pkg" +) + +type ListStore struct { + Cursor *payments.V3ConnectorScheduleInstancesCursorResponseCursor `json:"cursor"` +} + +type ListController struct { + PaymentsVersion versions.Version + + store *ListStore +} + +func (c *ListController) SetVersion(version versions.Version) { + c.PaymentsVersion = version +} + +var _ fctl.Controller[*ListStore] = (*ListController)(nil) + +func NewListStore() *ListStore { + return &ListStore{ + Cursor: &payments.V3ConnectorScheduleInstancesCursorResponseCursor{}, + } +} + +func NewListController() *ListController { + return &ListController{ + store: NewListStore(), + } +} + +func NewListCommand() *cobra.Command { + c := NewListController() + return fctl.NewCommand("list ", + fctl.WithAliases("ls", "l"), + fctl.WithArgs(cobra.ExactArgs(2)), + fctl.WithValidArgsFunction(cobra.NoFileCompletions), + fctl.WithShortDescription("List connector schedule instances"), + fctl.WithCursorFlag(), + fctl.WithPageSizeFlag(), + fctl.WithController[*ListStore](c), + ) +} + +func (c *ListController) GetStore() *ListStore { + return c.store +} + +func (c *ListController) Run(cmd *cobra.Command, args []string) (fctl.Renderable, error) { + + _, profile, profileName, relyingParty, err := fctl.LoadAndAuthenticateCurrentProfile(cmd) + if err != nil { + return nil, err + } + + stackClient, err := fctl.NewStackClientFromFlags(cmd, relyingParty, fctl.NewPTermDialog(), profileName, *profile) + if err != nil { + return nil, err + } + + if err := versions.GetPaymentsVersion(cmd, args, c); err != nil { + return nil, err + } + + if c.PaymentsVersion.Major < versions.V3 { + return nil, fmt.Errorf("connector schedules are only supported in >= v3.0.0") + } + + connectorID, scheduleID := args[0], args[1] + cursor, err := fctl.GetCursor(cmd) + if err != nil { + return nil, err + } + pageSize, err := fctl.GetPageSize(cmd) + if err != nil { + return nil, err + } + + request := operations.V3ListConnectorScheduleInstancesRequest{ + ConnectorID: connectorID, + ScheduleID: scheduleID, + PageSize: fctl.Ptr(int64(pageSize)), + } + if cursor != "" { + request.Cursor = &cursor + } + + response, err := stackClient.Payments.V3.ListConnectorScheduleInstances(cmd.Context(), request) + if err != nil { + return nil, err + } + + if response.StatusCode >= 300 { + return nil, fmt.Errorf("unexpected status code: %d", response.StatusCode) + } + + if response.V3ConnectorScheduleInstancesCursorResponse == nil { + return nil, fmt.Errorf("unexpected response: %v", response) + } + + c.store.Cursor = &response.V3ConnectorScheduleInstancesCursorResponse.Cursor + + return c, nil +} + +func (c *ListController) Render(cmd *cobra.Command, args []string) error { + tableData := fctl.Map(c.store.Cursor.Data, func(i payments.V3Instance) []string { + errStr := "" + if i.Error != nil { + errStr = *i.Error + } + terminatedAt := "" + if i.TerminatedAt != nil { + terminatedAt = i.TerminatedAt.Format(time.RFC3339) + } + return []string{ + i.ID, + fmt.Sprintf("%t", i.Terminated), + i.CreatedAt.Format(time.RFC3339), + i.UpdatedAt.Format(time.RFC3339), + terminatedAt, + errStr, + } + }) + tableData = fctl.Prepend(tableData, []string{ + "ID", "Terminated", + "CreatedAt", "UpdatedAt", "TerminatedAt", "Error", + }) + if err := pterm.DefaultTable. + WithHasHeader(). + WithWriter(cmd.OutOrStdout()). + WithData(tableData). + Render(); err != nil { + return err + } + + return fctl.RenderCursor(cmd.OutOrStdout(), fctl.Cursor{ + HasMore: c.store.Cursor.HasMore, + PageSize: c.store.Cursor.PageSize, + Next: c.store.Cursor.Next, + Previous: c.store.Cursor.Previous, + }) +} diff --git a/cmd/payments/connectors/schedules/instances/root.go b/cmd/payments/connectors/schedules/instances/root.go new file mode 100644 index 00000000..76e90938 --- /dev/null +++ b/cmd/payments/connectors/schedules/instances/root.go @@ -0,0 +1,17 @@ +package instances + +import ( + "github.com/spf13/cobra" + + fctl "github.com/formancehq/fctl/v3/pkg" +) + +func NewInstancesCommand() *cobra.Command { + return fctl.NewCommand("instances", + fctl.WithAliases("inst"), + fctl.WithShortDescription("Manage connector schedule instances"), + fctl.WithChildCommands( + NewListCommand(), + ), + ) +} diff --git a/cmd/payments/connectors/schedules/list.go b/cmd/payments/connectors/schedules/list.go new file mode 100644 index 00000000..ec429dd3 --- /dev/null +++ b/cmd/payments/connectors/schedules/list.go @@ -0,0 +1,140 @@ +package schedules + +import ( + "fmt" + "time" + + "github.com/pterm/pterm" + "github.com/spf13/cobra" + + "github.com/formancehq/formance-sdk-go/v4/pkg/models/operations" + "github.com/formancehq/formance-sdk-go/v4/pkg/models/payments" + + "github.com/formancehq/fctl/v3/cmd/payments/versions" + fctl "github.com/formancehq/fctl/v3/pkg" +) + +type ListStore struct { + Cursor *payments.V3ConnectorSchedulesCursorResponseCursor `json:"cursor"` +} + +type ListController struct { + PaymentsVersion versions.Version + + store *ListStore +} + +func (c *ListController) SetVersion(version versions.Version) { + c.PaymentsVersion = version +} + +var _ fctl.Controller[*ListStore] = (*ListController)(nil) + +func NewListStore() *ListStore { + return &ListStore{ + Cursor: &payments.V3ConnectorSchedulesCursorResponseCursor{}, + } +} + +func NewListController() *ListController { + return &ListController{ + store: NewListStore(), + } +} + +func NewListCommand() *cobra.Command { + c := NewListController() + return fctl.NewCommand("list ", + fctl.WithAliases("ls", "l"), + fctl.WithArgs(cobra.ExactArgs(1)), + fctl.WithValidArgsFunction(cobra.NoFileCompletions), + fctl.WithShortDescription("List connector schedules"), + fctl.WithCursorFlag(), + fctl.WithPageSizeFlag(), + fctl.WithController[*ListStore](c), + ) +} + +func (c *ListController) GetStore() *ListStore { + return c.store +} + +func (c *ListController) Run(cmd *cobra.Command, args []string) (fctl.Renderable, error) { + + _, profile, profileName, relyingParty, err := fctl.LoadAndAuthenticateCurrentProfile(cmd) + if err != nil { + return nil, err + } + + stackClient, err := fctl.NewStackClientFromFlags(cmd, relyingParty, fctl.NewPTermDialog(), profileName, *profile) + if err != nil { + return nil, err + } + + if err := versions.GetPaymentsVersion(cmd, args, c); err != nil { + return nil, err + } + + if c.PaymentsVersion.Major < versions.V3 { + return nil, fmt.Errorf("connector schedules are only supported in >= v3.0.0") + } + + connectorID := args[0] + cursor, err := fctl.GetCursor(cmd) + if err != nil { + return nil, err + } + pageSize, err := fctl.GetPageSize(cmd) + if err != nil { + return nil, err + } + + request := operations.V3ListConnectorSchedulesRequest{ + ConnectorID: connectorID, + PageSize: fctl.Ptr(int64(pageSize)), + } + if cursor != "" { + request.Cursor = &cursor + } + + response, err := stackClient.Payments.V3.ListConnectorSchedules(cmd.Context(), request) + if err != nil { + return nil, err + } + + if response.StatusCode >= 300 { + return nil, fmt.Errorf("unexpected status code: %d", response.StatusCode) + } + + if response.V3ConnectorSchedulesCursorResponse == nil { + return nil, fmt.Errorf("unexpected response: %v", response) + } + + c.store.Cursor = &response.V3ConnectorSchedulesCursorResponse.Cursor + + return c, nil +} + +func (c *ListController) Render(cmd *cobra.Command, args []string) error { + tableData := fctl.Map(c.store.Cursor.Data, func(s payments.V3Schedule) []string { + return []string{ + s.ID, + s.CreatedAt.Format(time.RFC3339), + } + }) + tableData = fctl.Prepend(tableData, []string{"ID", "CreatedAt"}) + if err := pterm.DefaultTable. + WithHasHeader(). + WithWriter(cmd.OutOrStdout()). + WithData(tableData). + Render(); err != nil { + return err + } + + return fctl.RenderCursor(cmd.OutOrStdout(), fctl.Cursor{ + HasMore: c.store.Cursor.HasMore, + PageSize: c.store.Cursor.PageSize, + Next: c.store.Cursor.Next, + Previous: c.store.Cursor.Previous, + }) +} diff --git a/cmd/payments/connectors/schedules/root.go b/cmd/payments/connectors/schedules/root.go new file mode 100644 index 00000000..1ed7cc0d --- /dev/null +++ b/cmd/payments/connectors/schedules/root.go @@ -0,0 +1,20 @@ +package schedules + +import ( + "github.com/spf13/cobra" + + "github.com/formancehq/fctl/v3/cmd/payments/connectors/schedules/instances" + fctl "github.com/formancehq/fctl/v3/pkg" +) + +func NewSchedulesCommand() *cobra.Command { + return fctl.NewCommand("schedules", + fctl.WithAliases("sch"), + fctl.WithShortDescription("Manage connector schedules"), + fctl.WithChildCommands( + NewListCommand(), + NewShowCommand(), + instances.NewInstancesCommand(), + ), + ) +} diff --git a/cmd/payments/connectors/schedules/show.go b/cmd/payments/connectors/schedules/show.go new file mode 100644 index 00000000..a1e6f4ce --- /dev/null +++ b/cmd/payments/connectors/schedules/show.go @@ -0,0 +1,121 @@ +package schedules + +import ( + "fmt" + "time" + + "github.com/pterm/pterm" + "github.com/spf13/cobra" + + "github.com/formancehq/formance-sdk-go/v4/pkg/models/operations" + "github.com/formancehq/formance-sdk-go/v4/pkg/models/payments" + + "github.com/formancehq/fctl/v3/cmd/payments/versions" + fctl "github.com/formancehq/fctl/v3/pkg" +) + +type ShowStore struct { + Schedule *payments.V3Schedule `json:"schedule"` +} + +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.WithAliases("sh", "s"), + fctl.WithArgs(cobra.ExactArgs(2)), + fctl.WithValidArgsFunction(cobra.NoFileCompletions), + fctl.WithShortDescription("Get a connector schedule"), + fctl.WithController[*ShowStore](c), + ) +} + +func (c *ShowController) GetStore() *ShowStore { + return c.store +} + +func (c *ShowController) Run(cmd *cobra.Command, args []string) (fctl.Renderable, error) { + + _, profile, profileName, relyingParty, err := fctl.LoadAndAuthenticateCurrentProfile(cmd) + if err != nil { + return nil, err + } + + stackClient, err := fctl.NewStackClientFromFlags(cmd, relyingParty, fctl.NewPTermDialog(), profileName, *profile) + if err != nil { + return nil, err + } + + if err := versions.GetPaymentsVersion(cmd, args, c); err != nil { + return nil, err + } + + if c.PaymentsVersion.Major < versions.V3 { + return nil, fmt.Errorf("connector schedules are only supported in >= v3.0.0") + } + + connectorID, scheduleID := args[0], args[1] + + response, err := stackClient.Payments.V3.GetConnectorSchedule(cmd.Context(), operations.V3GetConnectorScheduleRequest{ + ConnectorID: connectorID, + ScheduleID: scheduleID, + }) + if err != nil { + return nil, err + } + + if response.StatusCode >= 300 { + return nil, fmt.Errorf("unexpected status code: %d", response.StatusCode) + } + + if response.V3ConnectorScheduleResponse == nil { + return nil, fmt.Errorf("unexpected response: %v", response) + } + + c.store.Schedule = &response.V3ConnectorScheduleResponse.V3Schedule + + return c, nil +} + +func (c *ShowController) Render(cmd *cobra.Command, args []string) error { + s := c.store.Schedule + + fctl.Section.WithWriter(cmd.OutOrStdout()).Println("Information") + tableData := pterm.TableData{ + {pterm.LightCyan("ID"), s.ID}, + {pterm.LightCyan("ConnectorID"), s.ConnectorID}, + {pterm.LightCyan("CreatedAt"), s.CreatedAt.Format(time.RFC3339)}, + } + if s.PausedAt != nil { + tableData = append(tableData, []string{pterm.LightCyan("PausedAt"), s.PausedAt.Format(time.RFC3339)}) + } + if s.PausedReason != nil { + tableData = append(tableData, []string{pterm.LightCyan("PausedReason"), *s.PausedReason}) + } + + return pterm.DefaultTable. + WithWriter(cmd.OutOrStdout()). + WithData(tableData). + Render() +}