diff --git a/internal/cmd/stack/list.go b/internal/cmd/stack/list.go index d753625..911b727 100644 --- a/internal/cmd/stack/list.go +++ b/internal/cmd/stack/list.go @@ -162,7 +162,7 @@ func searchAllStacks(ctx context.Context, input structs.SearchInput) ([]stack, e ) } - result, err := searchStacks(ctx, pageInput) + result, err := searchStacks[stack](ctx, pageInput) if err != nil { return nil, err } @@ -179,6 +179,24 @@ func searchAllStacks(ctx context.Context, input structs.SearchInput) ([]stack, e return out, nil } +type hasIDAndName interface { + GetID() string + GetName() string +} + +type stackID struct { + ID string `graphql:"id" json:"id,omitempty"` + Name string `graphql:"name" json:"name,omitempty"` +} + +func (s stackID) GetID() string { + return s.ID +} + +func (s stackID) GetName() string { + return s.Name +} + type stack struct { ID string `graphql:"id" json:"id,omitempty"` Administrative bool `graphql:"administrative" json:"administrative,omitempty"` @@ -237,3 +255,11 @@ type stack struct { Name string `graphql:"name" json:"name,omitempty"` } `graphql:"workerPool" json:"workerPool,omitempty"` } + +func (s stack) GetID() string { + return s.ID +} + +func (s stack) GetName() string { + return s.Name +} diff --git a/internal/cmd/stack/local_preview.go b/internal/cmd/stack/local_preview.go index 47c2e72..e80b2cb 100644 --- a/internal/cmd/stack/local_preview.go +++ b/internal/cmd/stack/local_preview.go @@ -33,7 +33,7 @@ func localPreview() cli.ActionFunc { }) } - stack, err := getStack(cliCtx) + stack, err := getStack[stack](cliCtx) if err != nil { return err } diff --git a/internal/cmd/stack/open_command.go b/internal/cmd/stack/open_command.go index eec44e8..2d9c3ee 100644 --- a/internal/cmd/stack/open_command.go +++ b/internal/cmd/stack/open_command.go @@ -60,7 +60,7 @@ func openCommandInBrowser(cliCtx *cli.Context) error { } func findAndOpenStackInBrowser(ctx context.Context, p *stackSearchParams) error { - got, err := findAndSelectStack(ctx, p, false) + got, err := findAndSelectStack[stack](ctx, p, false) if errors.Is(err, errNoStackFound) { return errors.New("No stacks using the provided search parameters, maybe it's in a different subdir?") } @@ -163,16 +163,16 @@ type stackSearchParams struct { branch *string } -type searchStacksResult struct { - Stacks []stack +type searchStacksResult[T hasIDAndName] struct { + Stacks []T PageInfo structs.PageInfo } -func searchStacks(ctx context.Context, input structs.SearchInput) (searchStacksResult, error) { +func searchStacks[T hasIDAndName](ctx context.Context, input structs.SearchInput) (searchStacksResult[T], error) { var query struct { SearchStacksOutput struct { Edges []struct { - Node stack `graphql:"node"` + Node T `graphql:"node"` } `graphql:"edges"` PageInfo structs.PageInfo `graphql:"pageInfo"` } `graphql:"searchStacks(input: $input)"` @@ -184,15 +184,15 @@ func searchStacks(ctx context.Context, input structs.SearchInput) (searchStacksR map[string]interface{}{"input": input}, graphql.WithHeader("Spacelift-GraphQL-Query", "StacksPage"), ); err != nil { - return searchStacksResult{}, errors.Wrap(err, "failed search for stacks") + return searchStacksResult[T]{}, errors.Wrap(err, "failed search for stacks") } - stacks := make([]stack, 0) + stacks := make([]T, 0) for _, q := range query.SearchStacksOutput.Edges { stacks = append(stacks, q.Node) } - return searchStacksResult{ + return searchStacksResult[T]{ Stacks: stacks, PageInfo: query.SearchStacksOutput.PageInfo, }, nil diff --git a/internal/cmd/stack/stack_selector.go b/internal/cmd/stack/stack_selector.go index 6bea736..6a7b760 100644 --- a/internal/cmd/stack/stack_selector.go +++ b/internal/cmd/stack/stack_selector.go @@ -28,7 +28,7 @@ const ( // 2. Check the --run flag, if set, try to get the stack associated with the run. // 2. Check the current directory to determine repository and subdirectory and search for a stack. func getStackID(cliCtx *cli.Context) (string, error) { - stack, err := getStack(cliCtx) + stack, err := getStack[stackID](cliCtx) if err != nil { return "", err } @@ -36,10 +36,10 @@ func getStackID(cliCtx *cli.Context) (string, error) { return stack.ID, nil } -func getStack(cliCtx *cli.Context) (*stack, error) { +func getStack[T hasIDAndName](cliCtx *cli.Context) (*T, error) { if cliCtx.IsSet(flagStackID.Name) { stackID := cliCtx.String(flagStackID.Name) - stack, err := stackGetByID(cliCtx.Context, stackID) + stack, err := stackGetByID[T](cliCtx.Context, stackID) if errors.Is(err, errNoStackFound) { return nil, fmt.Errorf("stack with id %q could not be found. Please check that the stack exists and that you have access to it. To list available stacks run: spacectl stack list", stackID) } @@ -50,7 +50,7 @@ func getStack(cliCtx *cli.Context) (*stack, error) { return stack, nil } else if cliCtx.IsSet(flagRun.Name) { runID := cliCtx.String(flagRun.Name) - stack, err := stackGetByRunID(cliCtx.Context, runID) + stack, err := stackGetByRunID[T](cliCtx.Context, runID) if errors.Is(err, errNoStackFound) { return nil, fmt.Errorf("run with id %q was not found. Please check that the run exists and that you have access to it. To list available stacks run: spacectl stack run list", runID) } @@ -73,7 +73,7 @@ func getStack(cliCtx *cli.Context) (*stack, error) { skip := os.Getenv(envPromptSkipKey) == "true" - got, err := findAndSelectStack(cliCtx.Context, &stackSearchParams{ + got, err := findAndSelectStack[T](cliCtx.Context, &stackSearchParams{ count: 50, projectRoot: &subdir, repositoryName: name, @@ -89,11 +89,9 @@ func getStack(cliCtx *cli.Context) (*stack, error) { return got, nil } -func stackGetByID(ctx context.Context, stackID string) (*stack, error) { +func stackGetByID[T hasIDAndName](ctx context.Context, stackID string) (*T, error) { var query struct { - Stack struct { - stack - } `graphql:"stack(id: $id)"` + Stack T `graphql:"stack(id: $id)"` } variables := map[string]interface{}{ @@ -105,18 +103,16 @@ func stackGetByID(ctx context.Context, stackID string) (*stack, error) { return nil, fmt.Errorf("failed to query GraphQL API when checking if a stack exists: %w", err) } - if query.Stack.ID != stackID { + if query.Stack.GetID() != stackID { return nil, errNoStackFound } - return &query.Stack.stack, nil + return &query.Stack, nil } -func stackGetByRunID(ctx context.Context, runID string) (*stack, error) { +func stackGetByRunID[T hasIDAndName](ctx context.Context, runID string) (*T, error) { var query struct { - RunStack struct { - stack - } `graphql:"runStack(runId: $runId)"` + RunStack T `graphql:"runStack(runId: $runId)"` } variables := map[string]interface{}{ @@ -132,10 +128,10 @@ func stackGetByRunID(ctx context.Context, runID string) (*stack, error) { return nil, fmt.Errorf("failed to query GraphQL API when getting stack by run id: %w", err) } - return &query.RunStack.stack, nil + return &query.RunStack, nil } -func findAndSelectStack(ctx context.Context, p *stackSearchParams, forcePrompt bool) (*stack, error) { +func findAndSelectStack[T hasIDAndName](ctx context.Context, p *stackSearchParams, forcePrompt bool) (*T, error) { conditions := []structs.QueryPredicate{ { Field: graphql.String("repository"), @@ -169,16 +165,16 @@ func findAndSelectStack(ctx context.Context, p *stackSearchParams, forcePrompt b Predicates: &conditions, } - result, err := searchStacks(ctx, input) + result, err := searchStacks[T](ctx, input) if err != nil { return nil, err } items := []string{} - found := map[string]stack{} + found := map[string]T{} for _, s := range result.Stacks { - items = append(items, s.Name) - found[s.Name] = s + items = append(items, s.GetName()) + found[s.GetName()] = s } if len(found) == 0 {