diff --git a/cmd/cluster.go b/cmd/cluster.go index 79647eb..bb103af 100644 --- a/cmd/cluster.go +++ b/cmd/cluster.go @@ -19,12 +19,12 @@ var getClustersCmd = &cobra.Command{ Args: cobra.MaximumNArgs(1), Run: func(cmd *cobra.Command, args []string) { if len(args) == 0 { - clusters, err := client.ListClusters(apiToken, baseURL) + response, err := client.ListClusters(apiToken, baseURL, 0, 0) if err != nil { fmt.Printf("Error listing clusters: %v\n", err) return } - if len(clusters) == 0 { + if len(response.Result) == 0 { fmt.Println("No clusters found.") return } @@ -41,7 +41,7 @@ var getClustersCmd = &cobra.Command{ {Number: 6, WidthMin: 10}, {Number: 7, WidthMin: 15}, }) - for _, cluster := range clusters { + for _, cluster := range response.Result { t.AppendRow(table.Row{ cluster.Name, cluster.KubeVersion, diff --git a/cmd/cluster_select.go b/cmd/cluster_select.go index 504562d..e4d5fa0 100644 --- a/cmd/cluster_select.go +++ b/cmd/cluster_select.go @@ -3,58 +3,67 @@ package cmd import ( "encoding/json" "fmt" - "github.com/manifoldco/promptui" "os" "path/filepath" "strings" "ankra/internal/client" + "github.com/manifoldco/promptui" "github.com/spf13/cobra" ) +type SelectableItem struct { + IsLoadMore bool + Cluster *client.ClusterListItem + Label string +} + var selectClusterCmd = &cobra.Command{ Use: "cluster", Aliases: []string{"clusters"}, Short: "Interactively select a cluster and save as active", Run: func(cmd *cobra.Command, args []string) { - clusters, err := client.ListClusters(apiToken, baseURL) - if err != nil { - fmt.Printf("Error listing clusters: %v\n", err) - return - } - if len(clusters) == 0 { - fmt.Println("No clusters available.") - return - } - templates := &promptui.SelectTemplates{ - Label: "{{ . }}:", - Active: "\U0001F336 {{ .Name | cyan }} ({{ .ID | red }})", - Inactive: " {{ .Name | cyan }} ({{ .ID | red }})", - Selected: "\U0001F336 {{ .Name | red }}", - } - searcher := func(input string, index int) bool { - cluster := clusters[index] - return strings.Contains(strings.ToLower(cluster.Name), strings.ToLower(input)) - } - prompt := promptui.Select{ - Label: "Select Cluster", - Items: clusters, - Templates: templates, - Searcher: searcher, - } - i, _, err := prompt.Run() - if err != nil { - fmt.Printf("Prompt failed: %v\n", err) - return - } - selectedCluster := clusters[i] - if err := saveSelectedCluster(selectedCluster); err != nil { - fmt.Printf("Failed to save selection: %v\n", err) - return + page := 1 + fetchedClusters := []client.ClusterListItem{} + startCursorPosition := 0 + + for { + response, err := client.ListClusters(apiToken, baseURL, page, 25) + if err != nil { + fmt.Printf("Error listing clusters: %v\n", err) + break + } + + if len(response.Result) == 0 { + fmt.Println("No clusters available.") + break + } + + prompt, selectableItems, updatedFetchedClusters := createListPromptUi(response, fetchedClusters, startCursorPosition) + fetchedClusters = updatedFetchedClusters + i, _, err := prompt.Run() + if err != nil { + fmt.Printf("Prompt failed: %v\n", err) + break + } + selectedItem := selectableItems[i] + if selectedItem.IsLoadMore { + startCursorPosition = i + page++ + continue + } else { + selectedCluster := *selectedItem.Cluster + if err := saveSelectedCluster(selectedCluster); err != nil { + fmt.Printf("Failed to save selection: %v\n", err) + return + } else { + fmt.Printf("Selected cluster: %s (ID: %s) is now active.\n", selectedCluster.Name, selectedCluster.ID) + fmt.Println("You can now run 'ankra get operations' or 'ankra get addons'.") + return + } + } } - fmt.Printf("Selected cluster: %s (ID: %s) is now active.\n", selectedCluster.Name, selectedCluster.ID) - fmt.Println("You can now run 'ankra get operations' or 'ankra get addons'.") }, } @@ -125,3 +134,40 @@ func init() { selectCmd.AddCommand(selectClusterCmd) selectCmd.AddCommand(clearSelectionCmd) } + +func createListPromptUi(response *client.ClusterListResponse, previousFetchedClusters []client.ClusterListItem, startCursorPosition int) (promptui.Select, []SelectableItem, []client.ClusterListItem) { + templates := &promptui.SelectTemplates{ + Label: "{{ . }}:", + Active: "\U0001F336 {{ .Label | cyan }} {{ if .Cluster }} {{.Cluster.ID | red }} {{ end }}", + Inactive: " {{ .Label | cyan }} {{ if .Cluster }} {{.Cluster.ID | red }} {{ end }}", + Selected: " {{ if .Cluster }} \u2705 {{ .Label | cyan }} {{.Cluster.ID | red }} {{ end }} {{ if .IsLoadMore }} \u23F3 Loading next page... {{ end }}", + } + selectableItems := []SelectableItem{} + + fetchedClusters := append(previousFetchedClusters, response.Result...) + + for _, cluster := range fetchedClusters { + currentCluster := cluster + selectableItems = append(selectableItems, SelectableItem{IsLoadMore: false, Cluster: ¤tCluster, Label: cluster.Name}) + } + + if response.Pagination.Page < response.Pagination.TotalPages { + selectableItems = append(selectableItems, SelectableItem{IsLoadMore: true, Cluster: nil, Label: "→ Load Next Page"}) + } + + searcher := func(input string, index int) bool { + cluster := fetchedClusters[index] + return strings.Contains(strings.ToLower(cluster.Name), strings.ToLower(input)) + } + + prompt := promptui.Select{ + Label: "Select Cluster", + Items: selectableItems, + CursorPos: startCursorPosition, + Templates: templates, + Searcher: searcher, + Size: response.Pagination.PageSize + 1, + } + + return prompt, selectableItems, fetchedClusters +} diff --git a/internal/client/clusters.go b/internal/client/clusters.go index 3cb5cab..70ba291 100644 --- a/internal/client/clusters.go +++ b/internal/client/clusters.go @@ -57,15 +57,33 @@ type ClusterWithStatusResponse struct { Result []ClusterWithStatus `json:"result"` } -func ListClusters(token, baseURL string) ([]ClusterListItem, error) { - url := strings.TrimRight(baseURL, "/") + "/api/v1/clusters" - var resp ClusterListResponse - if err := getJSON(url, token, &resp); err != nil { +func ListClusters(token string, baseURL string, page int, pageSize int) (*ClusterListResponse, error) { + if page == 0 { + page = 1 + } + if pageSize == 0 { + pageSize = 25 + } + + url := fmt.Sprintf("%s/api/v1/clusters?page=%d&page_size=%d", baseURL, page, pageSize) + fmt.Printf("CHECK %s", url) + var response *ClusterListResponse + if err := getJSON(url, token, &response); err != nil { return nil, err } - return resp.Result, nil + + return response, nil } +// func ListClusters(token, baseURL string) ([]ClusterListItem, error) { +// url := strings.TrimRight(baseURL, "/") + "/api/v1/clusters" +// var resp ClusterListResponse +// if err := getJSON(url, token, &resp); err != nil { +// return nil, err +// } +// return resp.Result, nil +// } + func GetCluster(token, baseURL, name string) (ClusterWithStatus, error) { url := fmt.Sprintf("%s/api/v1/clusters?cluster_name=%s", strings.TrimRight(baseURL, "/"), name)