Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/app/link.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ func LinkAppFooterSection(ctx context.Context, clients *shared.ClientFactory, ap

// promptExistingApp gathers details to represent app information
func promptExistingApp(ctx context.Context, clients *shared.ClientFactory) (types.App, *types.SlackAuth, error) {
slackAuth, err := prompts.PromptTeamSlackAuth(ctx, clients, "Select the existing app team")
slackAuth, err := prompts.PromptTeamSlackAuth(ctx, clients, "Select the existing app team", nil)
if err != nil {
return types.App{}, &types.SlackAuth{}, err
}
Expand Down
13 changes: 9 additions & 4 deletions cmd/sandbox/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,16 @@ var createCmdFlags createFlags

// templateNameToID maps user-friendly template names to integer IDs
var templateNameToID = map[string]int{
"default": 1, // The default template
"empty": 0, // The sandbox will be empty if the template param is not set
"default": 1, // The default template

// Additional partner sandbox template options
"finance": 2,
"hr": 3,
"it-incident-management": 4,
"customer-support": 5,
"sales": 6,
"marketing": 7,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR also adds mappings for the new partner templates

Copy link
Copy Markdown
Member

@mwbrooks mwbrooks Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @vegeris. In the future, it would be better for this to be a separate PR so that it can stand out in the GitHub Release Notes and Changelog.

suggestion: Can you please update the Changelog section of this PR Description so that the documentation team knows to document it on docs.slack.dev.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: @vegeris Can you please add these to the Long Description so that developers can discover the partner template Ids that are available to use?

Copy link
Copy Markdown
Contributor Author

@vegeris vegeris Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure if we would want to highlight it in the Long Description as the grand majority of users would be regular non-partner developers; for the list command for example, we only add the 'sandbox type' label to partner sandboxes to reduce visual noise for regular sandboxes

document it on docs.slack.dev

Speaking of, I'd like to double check the process for getting new commands documented. My current assumption was that we would have a brief announcement in the Changelog through this PR and that once these changes are included in a release, that I would post in #api-docs to get the commands documented on docs.slack.dev?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, if there's a strong preference I can apply the changes unrelated to removing the experiment flag first and then come back to this PR

}

func NewCreateCommand(clients *shared.ClientFactory) *cobra.Command {
Expand All @@ -60,9 +68,6 @@ func NewCreateCommand(clients *shared.ClientFactory) *cobra.Command {
{Command: "sandbox create --name test-box --password mypass --domain test-box --archive-date 2025-12-31", Meaning: "Create a sandbox that will be archived on a specific date"},
}),
Args: cobra.NoArgs,
PreRunE: func(cmd *cobra.Command, args []string) error {
return requireSandboxExperiment(clients)
},
RunE: func(cmd *cobra.Command, args []string) error {
return runCreateCommand(cmd, clients)
},
Expand Down
66 changes: 0 additions & 66 deletions cmd/sandbox/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
"testing"
"time"

"github.com/slackapi/slack-cli/internal/experiment"
"github.com/slackapi/slack-cli/internal/shared"
"github.com/slackapi/slack-cli/internal/shared/types"
"github.com/slackapi/slack-cli/test/testutil"
Expand All @@ -36,7 +35,6 @@ func TestCreateCommand(t *testing.T) {
testutil.TableTestCommand(t, testutil.CommandTests{
"create success": {
CmdArgs: []string{
"--experiment=sandboxes",
"--token", "xoxb-test-token",
"--name", "test-box",
"--domain", "test-box",
Expand All @@ -52,8 +50,6 @@ func TestCreateCommand(t *testing.T) {
cm.API.On("UsersInfo", mock.Anything, mock.Anything, mock.Anything).Return(&types.UserInfo{Profile: types.UserProfile{}}, nil)

cm.AddDefaultMocks()
cm.Config.ExperimentsFlag = []string{string(experiment.Sandboxes)}
cm.Config.LoadExperiments(ctx, cm.IO.PrintDebug)
},
ExpectedStdoutOutputs: []string{"T123", "https://test-box.slack.com", "Sandbox Created"},
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
Expand All @@ -63,7 +59,6 @@ func TestCreateCommand(t *testing.T) {
},
"create with derived domain": {
CmdArgs: []string{
"--experiment=sandboxes",
"--token", "xoxb-test-token",
"--name", "My Test Box",
"--domain", "my-test-box",
Expand All @@ -78,16 +73,13 @@ func TestCreateCommand(t *testing.T) {
Return("T789", "https://my-test-box.slack.com", nil)

cm.AddDefaultMocks()
cm.Config.ExperimentsFlag = []string{string(experiment.Sandboxes)}
cm.Config.LoadExperiments(ctx, cm.IO.PrintDebug)
},
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
cm.API.AssertCalled(t, "CreateSandbox", mock.Anything, "xoxb-test-token", "My Test Box", "my-test-box", "pass", "", "", 0, "", int64(0), false)
},
},
"create with a relative time-to-live value": {
CmdArgs: []string{
"--experiment=sandboxes",
"--token", "xoxb-test-token",
"--name", "tmp-box",
"--domain", "tmp-box",
Expand All @@ -103,16 +95,13 @@ func TestCreateCommand(t *testing.T) {
Return("T111", "https://tmp-box.slack.com", nil)

cm.AddDefaultMocks()
cm.Config.ExperimentsFlag = []string{string(experiment.Sandboxes)}
cm.Config.LoadExperiments(ctx, cm.IO.PrintDebug)
},
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
cm.API.AssertCalled(t, "CreateSandbox", mock.Anything, "xoxb-test-token", "tmp-box", "tmp-box", "pass", "", "", 0, "", mock.MatchedBy(func(v int64) bool { return v > 0 }), false)
},
},
"create API error": {
CmdArgs: []string{
"--experiment=sandboxes",
"--token", "xoxb-test-token",
"--name", "err-box",
"--domain", "err-box",
Expand All @@ -127,14 +116,11 @@ func TestCreateCommand(t *testing.T) {
Return("", "", errors.New("api_error"))

cm.AddDefaultMocks()
cm.Config.ExperimentsFlag = []string{string(experiment.Sandboxes)}
cm.Config.LoadExperiments(ctx, cm.IO.PrintDebug)
},
ExpectedErrorStrings: []string{"api_error"},
},
"create with 'default' template": {
CmdArgs: []string{
"--experiment=sandboxes",
"--token", "xoxb-test-token",
"--name", "tpl-box",
"--domain", "tpl-box",
Expand All @@ -150,8 +136,6 @@ func TestCreateCommand(t *testing.T) {
Return("T333", "https://tpl-box.slack.com", nil)

cm.AddDefaultMocks()
cm.Config.ExperimentsFlag = []string{string(experiment.Sandboxes)}
cm.Config.LoadExperiments(ctx, cm.IO.PrintDebug)
},
ExpectedStdoutOutputs: []string{"T333", "https://tpl-box.slack.com", "Sandbox Created"},
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
Expand All @@ -160,7 +144,6 @@ func TestCreateCommand(t *testing.T) {
},
"create with partner flag": {
CmdArgs: []string{
"--experiment=sandboxes",
"--token", "xoxb-test-token",
"--name", "partner-box",
"--domain", "partner-box",
Expand All @@ -176,8 +159,6 @@ func TestCreateCommand(t *testing.T) {
Return("T555", "https://partner-box.slack.com", nil)

cm.AddDefaultMocks()
cm.Config.ExperimentsFlag = []string{string(experiment.Sandboxes)}
cm.Config.LoadExperiments(ctx, cm.IO.PrintDebug)
},
ExpectedStdoutOutputs: []string{"T555", "https://partner-box.slack.com", "Sandbox Created"},
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
Expand All @@ -186,7 +167,6 @@ func TestCreateCommand(t *testing.T) {
},
"create with invalid template fails": {
CmdArgs: []string{
"--experiment=sandboxes",
"--token", "xoxb-test-token",
"--name", "tmpl-box",
"--domain", "tmpl-box",
Expand All @@ -200,8 +180,6 @@ func TestCreateCommand(t *testing.T) {
cm.Auth.On("ResolveLogstashHost", mock.Anything, mock.Anything).Return("https://slackb.com/events/cli")

cm.AddDefaultMocks()
cm.Config.ExperimentsFlag = []string{string(experiment.Sandboxes)}
cm.Config.LoadExperiments(ctx, cm.IO.PrintDebug)
},
ExpectedErrorStrings: []string{"Invalid template"},
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
Expand All @@ -210,7 +188,6 @@ func TestCreateCommand(t *testing.T) {
},
"create with archive-date": {
CmdArgs: []string{
"--experiment=sandboxes",
"--token", "xoxb-test-token",
"--name", "date-box",
"--domain", "date-box",
Expand All @@ -226,16 +203,13 @@ func TestCreateCommand(t *testing.T) {
Return("T222", "https://date-box.slack.com", nil)

cm.AddDefaultMocks()
cm.Config.ExperimentsFlag = []string{string(experiment.Sandboxes)}
cm.Config.LoadExperiments(ctx, cm.IO.PrintDebug)
},
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
cm.API.AssertCalled(t, "CreateSandbox", mock.Anything, "xoxb-test-token", "date-box", "date-box", "pass", "", "", 0, "", archiveEpoch, false)
},
},
"create with both archive and archive-date fails": {
CmdArgs: []string{
"--experiment=sandboxes",
"--token", "xoxb-test-token",
"--name", "tmp-box",
"--domain", "tmp-box",
Expand All @@ -250,8 +224,6 @@ func TestCreateCommand(t *testing.T) {
cm.Auth.On("ResolveLogstashHost", mock.Anything, mock.Anything).Return("https://slackb.com/events/cli")

cm.AddDefaultMocks()
cm.Config.ExperimentsFlag = []string{string(experiment.Sandboxes)}
cm.Config.LoadExperiments(ctx, cm.IO.PrintDebug)
},
ExpectedErrorStrings: []string{"Cannot use both --archive-ttl and --archive-date"},
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
Expand All @@ -260,7 +232,6 @@ func TestCreateCommand(t *testing.T) {
},
"create with invalid archive-ttl value fails": {
CmdArgs: []string{
"--experiment=sandboxes",
"--token", "xoxb-test-token",
"--name", "tmp-box",
"--domain", "tmp-box",
Expand All @@ -274,28 +245,12 @@ func TestCreateCommand(t *testing.T) {
cm.Auth.On("ResolveLogstashHost", mock.Anything, mock.Anything).Return("https://slackb.com/events/cli")

cm.AddDefaultMocks()
cm.Config.ExperimentsFlag = []string{string(experiment.Sandboxes)}
cm.Config.LoadExperiments(ctx, cm.IO.PrintDebug)
},
ExpectedErrorStrings: []string{"Invalid TTL"},
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
cm.API.AssertNotCalled(t, "CreateSandbox", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything)
},
},
"experiment required": {
CmdArgs: []string{
"--name", "test-box",
"--domain", "test-box",
"--password", "pass",
},
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
cm.AddDefaultMocks()
},
ExpectedErrorStrings: []string{"sandbox"},
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
cm.API.AssertNotCalled(t, "CreateSandbox", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything)
},
},
}, func(cf *shared.ClientFactory) *cobra.Command {
return NewCreateCommand(cf)
})
Expand All @@ -310,8 +265,6 @@ func setupCreateMocks(t *testing.T, ctx context.Context, cm *shared.ClientsMock,
cm.API.On("CreateSandbox", mock.Anything, testToken, name, domain, password, "", "", 0, "", archiveEpoch, partner).
Return("T222", "https://"+domain+".slack.com", nil)
cm.AddDefaultMocks()
cm.Config.ExperimentsFlag = []string{string(experiment.Sandboxes)}
cm.Config.LoadExperiments(ctx, cm.IO.PrintDebug)
}

func setupCreateAuthOnly(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
Expand All @@ -321,15 +274,12 @@ func setupCreateAuthOnly(t *testing.T, ctx context.Context, cm *shared.ClientsMo
cm.Auth.On("ResolveAPIHost", mock.Anything, mock.Anything, mock.Anything).Return("https://api.slack.com")
cm.Auth.On("ResolveLogstashHost", mock.Anything, mock.Anything).Return("https://slackb.com/events/cli")
cm.AddDefaultMocks()
cm.Config.ExperimentsFlag = []string{string(experiment.Sandboxes)}
cm.Config.LoadExperiments(ctx, cm.IO.PrintDebug)
}

func Test_getEpochFromTTL(t *testing.T) {
testutil.TableTestCommand(t, testutil.CommandTests{
"1d": {
CmdArgs: []string{
"--experiment=sandboxes",
"--token", "xoxb-test-token",
"--name", "ttl-box",
"--domain", "ttl-box",
Expand All @@ -346,7 +296,6 @@ func Test_getEpochFromTTL(t *testing.T) {
},
"1w": {
CmdArgs: []string{
"--experiment=sandboxes",
"--token", "xoxb-test-token",
"--name", "ttl-box",
"--domain", "ttl-box",
Expand All @@ -363,7 +312,6 @@ func Test_getEpochFromTTL(t *testing.T) {
},
"6mo": {
CmdArgs: []string{
"--experiment=sandboxes",
"--token", "xoxb-test-token",
"--name", "ttl-box",
"--domain", "ttl-box",
Expand All @@ -380,7 +328,6 @@ func Test_getEpochFromTTL(t *testing.T) {
},
"invalid": {
CmdArgs: []string{
"--experiment=sandboxes",
"--token", "xoxb-test-token",
"--name", "ttl-box",
"--domain", "ttl-box",
Expand Down Expand Up @@ -408,7 +355,6 @@ func Test_getEpochFromDate(t *testing.T) {
testutil.TableTestCommand(t, testutil.CommandTests{
"valid": {
CmdArgs: []string{
"--experiment=sandboxes",
"--token", "xoxb-test-token",
"--name", "date-box",
"--domain", "date-box",
Expand All @@ -425,7 +371,6 @@ func Test_getEpochFromDate(t *testing.T) {
},
"invalid format": {
CmdArgs: []string{
"--experiment=sandboxes",
"--token", "xoxb-test-token",
"--name", "date-box",
"--domain", "date-box",
Expand All @@ -442,7 +387,6 @@ func Test_getEpochFromDate(t *testing.T) {
},
"invalid date": {
CmdArgs: []string{
"--experiment=sandboxes",
"--token", "xoxb-test-token",
"--name", "date-box",
"--domain", "date-box",
Expand All @@ -459,7 +403,6 @@ func Test_getEpochFromDate(t *testing.T) {
},
"date in past": {
CmdArgs: []string{
"--experiment=sandboxes",
"--token", "xoxb-test-token",
"--name", "date-box",
"--domain", "date-box",
Expand All @@ -483,7 +426,6 @@ func Test_getTemplateID(t *testing.T) {
testutil.TableTestCommand(t, testutil.CommandTests{
"valid template name": {
CmdArgs: []string{
"--experiment=sandboxes",
"--token", "xoxb-test-token",
"--name", "tpl-box",
"--domain", "tpl-box",
Expand All @@ -498,8 +440,6 @@ func Test_getTemplateID(t *testing.T) {
cm.API.On("CreateSandbox", mock.Anything, testToken, "tpl-box", "tpl-box", "pass", "", "", 1, "", int64(0), false).
Return("T333", "https://tpl-box.slack.com", nil)
cm.AddDefaultMocks()
cm.Config.ExperimentsFlag = []string{string(experiment.Sandboxes)}
cm.Config.LoadExperiments(ctx, cm.IO.PrintDebug)
},
ExpectedStdoutOutputs: []string{"Sandbox Created"},
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
Expand All @@ -508,7 +448,6 @@ func Test_getTemplateID(t *testing.T) {
},
"integer value also accepted": {
CmdArgs: []string{
"--experiment=sandboxes",
"--token", "xoxb-test-token",
"--name", "tpl-box",
"--domain", "tpl-box",
Expand All @@ -523,8 +462,6 @@ func Test_getTemplateID(t *testing.T) {
cm.API.On("CreateSandbox", mock.Anything, testToken, "tpl-box", "tpl-box", "pass", "", "", 1, "", int64(0), false).
Return("T333", "https://tpl-box.slack.com", nil)
cm.AddDefaultMocks()
cm.Config.ExperimentsFlag = []string{string(experiment.Sandboxes)}
cm.Config.LoadExperiments(ctx, cm.IO.PrintDebug)
},
ExpectedStdoutOutputs: []string{"Sandbox Created"},
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
Expand All @@ -533,7 +470,6 @@ func Test_getTemplateID(t *testing.T) {
},
"invalid template name fails": {
CmdArgs: []string{
"--experiment=sandboxes",
"--token", "xoxb-test-token",
"--name", "tpl-box",
"--domain", "tpl-box",
Expand All @@ -557,7 +493,6 @@ func Test_domainFromName(t *testing.T) {
testutil.TableTestCommand(t, testutil.CommandTests{
"handles invalid URL characters": {
CmdArgs: []string{
"--experiment=sandboxes",
"--token", "xoxb-test-token",
"--name", "-Hello_World 123-",
"--password", "pass",
Expand All @@ -572,7 +507,6 @@ func Test_domainFromName(t *testing.T) {
},
"empty": {
CmdArgs: []string{
"--experiment=sandboxes",
"--token", "xoxb-test-token",
"--name", "",
"--password", "pass",
Expand Down
3 changes: 0 additions & 3 deletions cmd/sandbox/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ func NewDeleteCommand(clients *shared.ClientFactory) *cobra.Command {
{Command: "sandbox delete --sandbox-id E0123456", Meaning: "Delete a sandbox identified by its team ID"},
}),
Args: cobra.NoArgs,
PreRunE: func(cmd *cobra.Command, args []string) error {
return requireSandboxExperiment(clients)
},
RunE: func(cmd *cobra.Command, args []string) error {
return runDeleteCommand(cmd, clients)
},
Expand Down
Loading
Loading