Skip to content
Merged
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
13 changes: 12 additions & 1 deletion cli/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func (a *API) Merge(other API) {
}

var loaders []Loader
var operationCommandHooks []func(*cobra.Command)

// Loader is used to detect and load an API spec, turning it into CLI commands.
type Loader interface {
Expand All @@ -58,6 +59,12 @@ func AddLoader(loader Loader) {
loaders = append(loaders, loader)
}

// AddOperationCommandHook registers a callback that can customize generated
// operation commands before they are added to the Cobra command tree.
func AddOperationCommandHook(hook func(*cobra.Command)) {
operationCommandHooks = append(operationCommandHooks, hook)
}

func setupRootFromAPI(root *cobra.Command, api *API) {
if root.Short == "" {
root.Short = api.Short
Expand All @@ -73,7 +80,11 @@ func setupRootFromAPI(root *cobra.Command, api *API) {
group := &cobra.Group{ID: op.Group, Title: groupName}
root.AddGroup(group)
}
root.AddCommand(op.Command())
cmd := op.Command()
for _, hook := range operationCommandHooks {
hook(cmd)
}
root.AddCommand(cmd)
}
}

Expand Down
42 changes: 42 additions & 0 deletions cli/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,45 @@ func TestBadSpecURL(t *testing.T) {
_, err := Load("https://api.example.com", &cobra.Command{})
assert.Error(t, err)
}

func TestLoadAppliesOperationCommandHooks(t *testing.T) {
reset(false)
viper.Set("rsh-no-cache", true)

called := false
AddOperationCommandHook(func(cmd *cobra.Command) {
if cmd.Name() != "search-web" {
return
}
called = true
cmd.Flags().Bool("hooked", false, "hooked by test")
})

AddLoader(&overrideLoader{
load: func(entrypoint, spec url.URL, resp *http.Response) (API, error) {
return API{
Operations: []Operation{
{
Name: "search web",
Method: http.MethodGet,
URITemplate: "https://api.example.com/search",
},
},
}, nil
},
})

configs["hook-test"] = &APIConfig{
Base: "https://api.example.com",
SpecFiles: []string{"testdata/petstore.json"},
}

root := &cobra.Command{Use: "test"}
_, err := Load("https://api.example.com", root)
assert.NoError(t, err)
assert.True(t, called)

cmd, _, err := root.Find([]string{"search-web"})
assert.NoError(t, err)
assert.NotNil(t, cmd.Flags().Lookup("hooked"))
}
1 change: 1 addition & 0 deletions cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ func Init(name string, version string) {
encodings = map[string]ContentEncoding{}
linkParsers = []LinkParser{}
loaders = []Loader{}
operationCommandHooks = nil

// Determine if we are using a TTY or colored output is forced-on.
tty := false
Expand Down
3 changes: 2 additions & 1 deletion cmd/surf/compat_flags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ func TestHiddenCompatRootFlags(t *testing.T) {

func TestSearchWebQueryCompatAlias(t *testing.T) {
cmd := &cobra.Command{
Use: "search-web",
Use: "search-web [flags]",
Run: func(cmd *cobra.Command, args []string) {},
}
cmd.Flags().String("q", "", "query")
addHiddenCompatOperationFlags(cmd)
addHiddenCompatOperationFlags(cmd)

f := cmd.Flags().Lookup("query")
if f == nil {
Expand Down
17 changes: 16 additions & 1 deletion cmd/surf/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ func main() {
cli.Init("surf", version)
cli.Defaults()
cli.AddLoader(openapi.New())
cli.AddOperationCommandHook(addHiddenCompatOperationFlags)

// Send Cobra diagnostics (deprecation warnings, usage errors) to stderr
// so they don't pollute JSON output on stdout.
Expand Down Expand Up @@ -153,6 +154,9 @@ func main() {
// Errors are still printed by cli.Run()'s error handling.
cmd.SilenceErrors = true
cmd.SilenceUsage = true
for _, child := range cmd.Commands() {
addHiddenCompatOperationFlags(child)
}

// Catch unknown commands that fall through to the API subcommand.
// Without this, `surf nonexistent` shows the API subcommand's
Expand Down Expand Up @@ -253,7 +257,10 @@ func addHiddenCompatRootFlags(root *cobra.Command) {
}

func addHiddenCompatOperationFlags(cmd *cobra.Command) {
if cmd.Name() != "search-web" {
if !isSearchWebCommand(cmd) {
return
}
if cmd.Flags().Lookup("query") != nil {
return
}

Expand Down Expand Up @@ -281,6 +288,14 @@ func addHiddenCompatOperationFlags(cmd *cobra.Command) {
}
}

func isSearchWebCommand(cmd *cobra.Command) bool {
if cmd.Name() == "search-web" {
return true
}
fields := strings.Fields(cmd.Use)
return len(fields) > 0 && fields[0] == "search-web"
}

// needsCachedAPI reports whether the current argv invokes a command that
// requires the cached OpenAPI spec. Meta commands (auth, sync, help, version,
// install, completion, telemetry, feedback, catalog) work without it.
Expand Down
Loading