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
65 changes: 15 additions & 50 deletions cmd/docs/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@
package docs

import (
"fmt"
"strings"

"github.com/slackapi/slack-cli/internal/shared"
"github.com/slackapi/slack-cli/internal/slackerror"
"github.com/slackapi/slack-cli/internal/slacktrace"
Expand All @@ -27,8 +24,6 @@ import (

const docsURL = "https://docs.slack.dev"

var searchMode bool

func NewCommand(clients *shared.ClientFactory) *cobra.Command {
cmd := &cobra.Command{
Use: "docs",
Expand All @@ -48,76 +43,46 @@ func NewCommand(clients *shared.ClientFactory) *cobra.Command {
Command: "docs search \"Block Kit\" --output=browser",
},
}),
Args: cobra.ArbitraryArgs, // Allow any arguments
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return runDocsCommand(clients, cmd, args)
return runDocsCommand(clients, cmd)
},
// Disable automatic suggestions for unknown commands
DisableSuggestions: true,
}

cmd.Flags().BoolVar(&searchMode, "search", false, "open Slack docs search page or search with query")

// Add the search subcommand
cmd.AddCommand(NewSearchCommand(clients))

// Catch removed --search flag
cmd.Flags().BoolP("search", "", false, "DEPRECATED: use 'docs search' subcommand instead")

return cmd
}

// runDocsCommand opens Slack developer docs in the browser
func runDocsCommand(clients *shared.ClientFactory, cmd *cobra.Command, args []string) error {
func runDocsCommand(clients *shared.ClientFactory, cmd *cobra.Command) error {
ctx := cmd.Context()

var finalURL string
var sectionText string

// Validate: if there are arguments, search subcommand must be used
if len(args) > 0 && !cmd.Flags().Changed("search") {
query := strings.Join(args, " ")
return slackerror.New(slackerror.ErrDocsSearchFlagRequired).WithMessage(
"Invalid docs command. Did you mean to search?",
if cmd.Flags().Changed("search") {
return slackerror.New("docs_search_flag_removed").WithMessage(
"The --search flag has been removed.",
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.

Claude really didn't like me doing this but I think it's kinda polite?

).WithRemediation(
"Use search subcommand: %s",
style.Commandf(fmt.Sprintf("docs search \"%s\"", query), false),
"Use the search subcommand instead: %s",
style.Commandf("docs search \"<query>\"", false),
)
}

if cmd.Flags().Changed("search") {
if len(args) > 0 {
// --search "query" (space-separated) - join all args as the query
query := strings.Join(args, " ")
finalURL = buildDocsSearchURL(query)
sectionText = "Docs Search"
} else {
// --search (no argument) - open search page
finalURL = fmt.Sprintf("%s/search/", docsURL)
sectionText = "Docs Search"
}
} else {
// No search flag: default homepage
finalURL = docsURL
sectionText = "Docs Open"
}

clients.IO.PrintInfo(ctx, false, "\n%s", style.Sectionf(style.TextSection{
Emoji: "books",
Text: sectionText,
Text: "Docs Open",
Secondary: []string{
finalURL,
docsURL,
},
}))

clients.Browser().OpenURL(finalURL)

if cmd.Flags().Changed("search") {
traceValue := ""
if len(args) > 0 {
traceValue = strings.Join(args, " ")
}
clients.IO.PrintTrace(ctx, slacktrace.DocsSearchSuccess, traceValue)
} else {
clients.IO.PrintTrace(ctx, slacktrace.DocsSuccess)
}
clients.Browser().OpenURL(docsURL)
clients.IO.PrintTrace(ctx, slacktrace.DocsSuccess)

return nil
}
92 changes: 6 additions & 86 deletions cmd/docs/docs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import (

func Test_Docs_DocsCommand(t *testing.T) {
testutil.TableTestCommand(t, testutil.CommandTests{
"opens docs homepage without search": {
"opens docs homepage": {
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
},
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
Expand All @@ -40,93 +40,13 @@ func Test_Docs_DocsCommand(t *testing.T) {
"https://docs.slack.dev",
},
},
"fails when positional argument provided without search flag": {
"rejects positional arguments": {
CmdArgs: []string{"Block Kit"},
ExpectedErrorStrings: []string{"Invalid docs command. Did you mean to search?"},
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
// No browser calls should be made when command fails
cm.Browser.AssertNotCalled(t, "OpenURL")
},
},
"fails when multiple positional arguments provided without search flag": {
CmdArgs: []string{"webhook", "send", "message"},
ExpectedErrorStrings: []string{"Invalid docs command. Did you mean to search?"},
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
// No browser calls should be made when command fails
cm.Browser.AssertNotCalled(t, "OpenURL")
},
},
"opens docs with search query using space syntax": {
CmdArgs: []string{"--search", "messaging"},
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
expectedURL := "https://docs.slack.dev/search/?q=messaging"
cm.Browser.AssertCalled(t, "OpenURL", expectedURL)
cm.IO.AssertCalled(t, "PrintTrace", mock.Anything, slacktrace.DocsSearchSuccess, mock.Anything)
},
ExpectedOutputs: []string{
"Docs Search",
"https://docs.slack.dev/search/?q=messaging",
},
},
"handles search with multiple arguments": {
CmdArgs: []string{"--search", "Block", "Kit", "Element"},
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
expectedURL := "https://docs.slack.dev/search/?q=Block+Kit+Element"
cm.Browser.AssertCalled(t, "OpenURL", expectedURL)
cm.IO.AssertCalled(t, "PrintTrace", mock.Anything, slacktrace.DocsSearchSuccess, mock.Anything)
},
ExpectedOutputs: []string{
"Docs Search",
"https://docs.slack.dev/search/?q=Block+Kit+Element",
},
},
"handles search query with multiple words": {
CmdArgs: []string{"--search", "socket mode"},
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
expectedURL := "https://docs.slack.dev/search/?q=socket+mode"
cm.Browser.AssertCalled(t, "OpenURL", expectedURL)
cm.IO.AssertCalled(t, "PrintTrace", mock.Anything, slacktrace.DocsSearchSuccess, mock.Anything)
},
ExpectedOutputs: []string{
"Docs Search",
"https://docs.slack.dev/search/?q=socket+mode",
},
},
"handles special characters in search query": {
CmdArgs: []string{"--search", "messages & webhooks"},
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
expectedURL := "https://docs.slack.dev/search/?q=messages+%26+webhooks"
cm.Browser.AssertCalled(t, "OpenURL", expectedURL)
cm.IO.AssertCalled(t, "PrintTrace", mock.Anything, slacktrace.DocsSearchSuccess, mock.Anything)
},
ExpectedOutputs: []string{
"Docs Search",
"https://docs.slack.dev/search/?q=messages+%26+webhooks",
},
},
"handles search query with quotes": {
CmdArgs: []string{"--search", "webhook \"send message\""},
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
expectedURL := "https://docs.slack.dev/search/?q=webhook+%22send+message%22"
cm.Browser.AssertCalled(t, "OpenURL", expectedURL)
cm.IO.AssertCalled(t, "PrintTrace", mock.Anything, slacktrace.DocsSearchSuccess, mock.Anything)
},
ExpectedOutputs: []string{
"Docs Search",
"https://docs.slack.dev/search/?q=webhook+%22send+message%22",
},
ExpectedErrorStrings: []string{"unknown command"},
},
"handles search flag without argument": {
CmdArgs: []string{"--search"},
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
expectedURL := "https://docs.slack.dev/search/"
cm.Browser.AssertCalled(t, "OpenURL", expectedURL)
cm.IO.AssertCalled(t, "PrintTrace", mock.Anything, slacktrace.DocsSearchSuccess, mock.Anything)
},
ExpectedOutputs: []string{
"Docs Search",
"https://docs.slack.dev/search/",
},
"rejects deprecated --search flag": {
CmdArgs: []string{"--search"},
ExpectedErrorStrings: []string{"The --search flag has been removed"},
},
}, func(cf *shared.ClientFactory) *cobra.Command {
return NewCommand(cf)
Expand Down
7 changes: 0 additions & 7 deletions internal/slackerror/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ const (
ErrDenoNotFound = "deno_not_found"
ErrDeployedAppNotSupported = "deployed_app_not_supported"
ErrDocumentationGenerationFailed = "documentation_generation_failed"
ErrDocsSearchFlagRequired = "docs_search_flag_required"
ErrDotEnvFileParse = "dotenv_file_parse_error"
ErrDotEnvFileRead = "dotenv_file_read_error"
ErrDotEnvFileWrite = "dotenv_file_write_error"
Expand Down Expand Up @@ -690,12 +689,6 @@ Otherwise start your app for local development with: %s`,
Message: "Failed to generate documentation",
},

ErrDocsSearchFlagRequired: {
Code: ErrDocsSearchFlagRequired,
Message: "Invalid docs command. Did you mean to search?",
Remediation: fmt.Sprintf("Use search subcommand: %s", style.Commandf("docs search \"<query>\"", false)),
},

ErrDotEnvFileParse: {
Code: ErrDotEnvFileParse,
Message: "Failed to parse the .env file",
Expand Down
Loading