Skip to content

Commit

Permalink
feat: Add limit and search flags to stack list command (#252)
Browse files Browse the repository at this point in the history
  • Loading branch information
0michalsokolowski0 committed Aug 30, 2024
1 parent 637a82d commit f73c926
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 12 deletions.
10 changes: 10 additions & 0 deletions internal/cmd/stack/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,13 @@ var flagInteractive = &cli.BoolFlag{
Aliases: []string{"i"},
Usage: "[Optional] Whether to run the command in interactive mode",
}

var flagLimit = &cli.UintFlag{
Name: "limit",
Usage: "[Optional] Limit the number of items to return",
}

var flagSearch = &cli.StringFlag{
Name: "search",
Usage: "[Optional] Performs a full-text search.",
}
104 changes: 92 additions & 12 deletions internal/cmd/stack/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ package stack
import (
"context"
"fmt"
"math"
"slices"
"strings"

"github.com/shurcooL/graphql"
"github.com/spacelift-io/spacectl/client/structs"
"github.com/spacelift-io/spacectl/internal"
"github.com/spacelift-io/spacectl/internal/cmd"
"github.com/urfave/cli/v2"
)
Expand All @@ -18,20 +21,57 @@ func listStacks() cli.ActionFunc {
return err
}

var limit *uint
if cliCtx.IsSet(flagLimit.Name) {
if cliCtx.Uint(flagLimit.Name) == 0 {
return fmt.Errorf("limit must be greater than 0")
}

if cliCtx.Uint(flagLimit.Name) >= math.MaxInt32 {
return fmt.Errorf("limit must be less than %d", math.MaxInt32)
}

limit = internal.Ptr(cliCtx.Uint(flagLimit.Name))
}

var search *string
if cliCtx.IsSet(flagSearch.Name) {
if cliCtx.String(flagSearch.Name) == "" {
return fmt.Errorf("search must be non-empty")
}

search = internal.Ptr(cliCtx.String(flagSearch.Name))
}

switch outputFormat {
case cmd.OutputFormatTable:
return listStacksTable(cliCtx)
return listStacksTable(cliCtx, search, limit)
case cmd.OutputFormatJSON:
return listStacksJSON(cliCtx)
return listStacksJSON(cliCtx, search, limit)
}

return fmt.Errorf("unknown output format: %v", outputFormat)
}
}

func listStacksJSON(ctx *cli.Context) error {
func listStacksJSON(
ctx *cli.Context,
search *string,
limit *uint,
) error {
var first *graphql.Int
if limit != nil {
first = graphql.NewInt(graphql.Int(*limit)) //nolint: gosec
}

var fullTextSearch *graphql.String
if search != nil {
fullTextSearch = graphql.NewString(graphql.String(*search))
}

stacks, err := searchAllStacks(ctx.Context, structs.SearchInput{
First: graphql.NewInt(50),
First: first,
FullTextSearch: fullTextSearch,
})
if err != nil {
return err
Expand All @@ -40,14 +80,31 @@ func listStacksJSON(ctx *cli.Context) error {
return cmd.OutputJSON(stacks)
}

func listStacksTable(ctx *cli.Context) error {
stacks, err := searchAllStacks(ctx.Context, structs.SearchInput{
First: graphql.NewInt(50),
func listStacksTable(
ctx *cli.Context,
search *string,
limit *uint,
) error {
var first *graphql.Int
if limit != nil {
first = graphql.NewInt(graphql.Int(*limit)) //nolint: gosec
}

var fullTextSearch *graphql.String
if search != nil {
fullTextSearch = graphql.NewString(graphql.String(*search))
}

input := structs.SearchInput{
First: first,
FullTextSearch: fullTextSearch,
OrderBy: &structs.QueryOrder{
Field: "starred",
Direction: "DESC",
},
})
}

stacks, err := searchAllStacks(ctx.Context, input)
if err != nil {
return err
}
Expand Down Expand Up @@ -78,19 +135,42 @@ func listStacksTable(ctx *cli.Context) error {
return cmd.OutputTable(tableData, true)
}

// searchStacks returns a list of stacks based on the provided search input.
// input.First limits the total number of returned stacks, if not provided all stacks are returned.
func searchAllStacks(ctx context.Context, input structs.SearchInput) ([]stack, error) {
out := []stack{}
const maxPageSize = 50

var limit int
if input.First != nil {
limit = int(*input.First)
}
fetchAll := limit == 0

out := []stack{}
pageInput := structs.SearchInput{
First: graphql.NewInt(maxPageSize),
FullTextSearch: input.FullTextSearch,
}
for {
result, err := searchStacks(ctx, input)
if !fetchAll {
// Fetch exactly the number of items requested
pageInput.First = graphql.NewInt(
//nolint: gosec
graphql.Int(
slices.Min([]int{maxPageSize, limit - len(out)}),
),
)
}

result, err := searchStacks(ctx, pageInput)
if err != nil {
return nil, err
}

out = append(out, result.Stacks...)

if result.PageInfo.HasNextPage {
input.After = graphql.NewString(graphql.String(result.PageInfo.EndCursor))
if result.PageInfo.HasNextPage && (fetchAll || limit > len(out)) {
pageInput.After = graphql.NewString(graphql.String(result.PageInfo.EndCursor))
} else {
break
}
Expand Down
2 changes: 2 additions & 0 deletions internal/cmd/stack/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ func Command() *cli.Command {
flagShowLabels,
cmd.FlagOutputFormat,
cmd.FlagNoColor,
flagLimit,
flagSearch,
},
Action: listStacks(),
Before: cmd.PerformAllBefore(cmd.HandleNoColor, authenticated.Ensure),
Expand Down
5 changes: 5 additions & 0 deletions internal/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package internal

func Ptr[T any](v T) *T {
return &v
}

0 comments on commit f73c926

Please sign in to comment.