Skip to content

Commit

Permalink
refactor: Use searchStacks over stacks for listing stacks (#246)
Browse files Browse the repository at this point in the history
  • Loading branch information
0michalsokolowski0 authored Aug 28, 2024
1 parent 9da88aa commit 4c916ad
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 86 deletions.
92 changes: 49 additions & 43 deletions internal/cmd/stack/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ package stack
import (
"context"
"fmt"
"sort"
"strings"

"github.com/pkg/errors"
"github.com/shurcooL/graphql"
"github.com/spacelift-io/spacectl/client/structs"
"github.com/spacelift-io/spacectl/internal/cmd"
"github.com/spacelift-io/spacectl/internal/cmd/authenticated"
"github.com/urfave/cli/v2"
)

Expand All @@ -23,68 +22,54 @@ func listStacks() cli.ActionFunc {
case cmd.OutputFormatTable:
return listStacksTable(cliCtx)
case cmd.OutputFormatJSON:
return listStacksJSON(cliCtx.Context)
return listStacksJSON(cliCtx)
}

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

func listStacksJSON(ctx context.Context) error {
var query struct {
Stacks []stack `graphql:"stacks" json:"stacks,omitempty"`
func listStacksJSON(ctx *cli.Context) error {
stacks, err := searchAllStacks(ctx.Context, structs.SearchInput{
First: graphql.NewInt(50),
})
if err != nil {
return err
}

if err := authenticated.Client.Query(ctx, &query, map[string]interface{}{}); err != nil {
return errors.Wrap(err, "failed to query list of stacks")
}
return cmd.OutputJSON(query.Stacks)
return cmd.OutputJSON(stacks)
}

func listStacksTable(ctx *cli.Context) error {
var query struct {
Stacks []struct {
ID string `graphql:"id" json:"id,omitempty"`
LockedBy string `graphql:"lockedBy"`
Name string `graphql:"name"`
State string `graphql:"state"`
Labels []string `graphql:"labels"`
TrackedCommit struct {
AuthorName string `graphql:"authorName"`
Hash string `graphql:"hash"`
} `graphql:"trackedCommit"`
WorkerPool struct {
Name string `graphql:"name"`
} `graphql:"workerPool"`
} `graphql:"stacks"`
}

if err := authenticated.Client.Query(ctx.Context, &query, map[string]interface{}{}); err != nil {
return errors.Wrap(err, "failed to query list of stacks")
}

sort.SliceStable(query.Stacks, func(i, j int) bool {
return strings.Compare(strings.ToLower(query.Stacks[i].Name), strings.ToLower(query.Stacks[j].Name)) < 0
stacks, err := searchAllStacks(ctx.Context, structs.SearchInput{
First: graphql.NewInt(50),
OrderBy: &structs.QueryOrder{
Field: "starred",
Direction: "DESC",
},
})
if err != nil {
return err
}

columns := []string{"Name", "ID", "Commit", "Author", "State", "Worker Pool", "Locked By"}
if ctx.Bool(flagShowLabels.Name) {
columns = append(columns, "Labels")
}

tableData := [][]string{columns}
for _, stack := range query.Stacks {
for _, s := range stacks {
row := []string{
stack.Name,
stack.ID,
cmd.HumanizeGitHash(stack.TrackedCommit.Hash),
stack.TrackedCommit.AuthorName,
stack.State,
stack.WorkerPool.Name,
stack.LockedBy,
s.Name,
s.ID,
cmd.HumanizeGitHash(s.TrackedCommit.Hash),
s.TrackedCommit.AuthorName,
s.State,
s.WorkerPool.Name,
s.LockedBy,
}
if ctx.Bool(flagShowLabels.Name) {
row = append(row, strings.Join(stack.Labels, ", "))
row = append(row, strings.Join(s.Labels, ", "))
}

tableData = append(tableData, row)
Expand All @@ -93,6 +78,27 @@ func listStacksTable(ctx *cli.Context) error {
return cmd.OutputTable(tableData, true)
}

func searchAllStacks(ctx context.Context, input structs.SearchInput) ([]stack, error) {
out := []stack{}

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

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

if result.PageInfo.HasNextPage {
input.After = graphql.NewString(graphql.String(result.PageInfo.EndCursor))
} else {
break
}
}

return out, nil
}

type stack struct {
ID string `graphql:"id" json:"id,omitempty"`
Administrative bool `graphql:"administrative" json:"administrative,omitempty"`
Expand Down
52 changes: 14 additions & 38 deletions internal/cmd/stack/open_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,12 @@ type stackSearchParams struct {
branch *string
}

func searchStacks(ctx context.Context, p *stackSearchParams) ([]stack, error) {
type searchStacksResult struct {
Stacks []stack
PageInfo structs.PageInfo
}

func searchStacks(ctx context.Context, input structs.SearchInput) (searchStacksResult, error) {
var query struct {
SearchStacksOutput struct {
Edges []struct {
Expand All @@ -172,52 +177,23 @@ func searchStacks(ctx context.Context, p *stackSearchParams) ([]stack, error) {
PageInfo structs.PageInfo `graphql:"pageInfo"`
} `graphql:"searchStacks(input: $input)"`
}
conditions := []structs.QueryPredicate{
{
Field: graphql.String("repository"),
Constraint: structs.QueryFieldConstraint{
StringMatches: &[]graphql.String{graphql.String(p.repositoryName)},
},
},
}

if p.projectRoot != nil && *p.projectRoot != "" {
root := strings.TrimSuffix(*p.projectRoot, "/")
conditions = append(conditions, structs.QueryPredicate{
Field: graphql.String("projectRoot"),
Constraint: structs.QueryFieldConstraint{
StringMatches: &[]graphql.String{graphql.String(root), graphql.String(root + "/")},
},
})
}

if p.branch != nil {
conditions = append(conditions, structs.QueryPredicate{
Field: graphql.String("branch"),
Constraint: structs.QueryFieldConstraint{
StringMatches: &[]graphql.String{graphql.String(*p.branch)},
},
})
}

variables := map[string]interface{}{"input": structs.SearchInput{
First: graphql.NewInt(graphql.Int(p.count)), //nolint: gosec
Predicates: &conditions,
}}

if err := authenticated.Client.Query(
ctx,
&query,
variables,
map[string]interface{}{"input": input},
graphql.WithHeader("Spacelift-GraphQL-Query", "StacksPage"),
); err != nil {
return nil, errors.Wrap(err, "failed search for stacks")
return searchStacksResult{}, errors.Wrap(err, "failed search for stacks")
}

result := make([]stack, 0)
stacks := make([]stack, 0)
for _, q := range query.SearchStacksOutput.Edges {
result = append(result, q.Node)
stacks = append(stacks, q.Node)
}

return result, nil
return searchStacksResult{
Stacks: stacks,
PageInfo: query.SearchStacksOutput.PageInfo,
}, nil
}
44 changes: 39 additions & 5 deletions internal/cmd/stack/stack_selector.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ import (
"github.com/manifoldco/promptui"
"github.com/pkg/errors"
"github.com/shurcooL/graphql"
"github.com/spacelift-io/spacectl/client/structs"
"github.com/spacelift-io/spacectl/internal/cmd/authenticated"
"github.com/urfave/cli/v2"
)

var errNoStackFound = errors.New("no stack found")

// getStackID will try to retreive a stack ID from multiple sources.
// getStackID will try to retrieve a stack ID from multiple sources.
// It will do so in the following order:
// 1. Check the --id flag, if set, use that value.
// 2. Check the current directory to determine repository and subdirectory and search for a stack.
Expand Down Expand Up @@ -90,16 +91,49 @@ func stackGetByID(ctx context.Context, stackID string) (*stack, error) {
}

func findAndSelectStack(ctx context.Context, p *stackSearchParams, forcePrompt bool) (*stack, error) {
stacks, err := searchStacks(ctx, p)
conditions := []structs.QueryPredicate{
{
Field: graphql.String("repository"),
Constraint: structs.QueryFieldConstraint{
StringMatches: &[]graphql.String{graphql.String(p.repositoryName)},
},
},
}

if p.projectRoot != nil && *p.projectRoot != "" {
root := strings.TrimSuffix(*p.projectRoot, "/")
conditions = append(conditions, structs.QueryPredicate{
Field: "projectRoot",
Constraint: structs.QueryFieldConstraint{
StringMatches: &[]graphql.String{graphql.String(root), graphql.String(root + "/")},
},
})
}

if p.branch != nil {
conditions = append(conditions, structs.QueryPredicate{
Field: "branch",
Constraint: structs.QueryFieldConstraint{
StringMatches: &[]graphql.String{graphql.String(*p.branch)},
},
})
}

input := structs.SearchInput{
First: graphql.NewInt(graphql.Int(p.count)), //nolint: gosec
Predicates: &conditions,
}

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

items := []string{}
found := map[string]stack{}
for _, stack := range stacks {
items = append(items, stack.Name)
found[stack.Name] = stack
for _, s := range result.Stacks {
items = append(items, s.Name)
found[s.Name] = s
}

if len(found) == 0 {
Expand Down

0 comments on commit 4c916ad

Please sign in to comment.