Skip to content

Commit b9d6da4

Browse files
committed
feat: completion of URI templates
1 parent 593acaf commit b9d6da4

File tree

1 file changed

+68
-13
lines changed

1 file changed

+68
-13
lines changed

cli/cli.go

+68-13
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,53 @@ func generic(method string, addr string, args []string) {
100100
MakeRequestAndFormat(req)
101101
}
102102

103-
func completeAPINames(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
103+
func completeCurrentConfig(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
104104
possible := []string{}
105+
if currentConfig != nil {
106+
for _, cmd := range Root.Commands() {
107+
if cmd.Use == currentConfig.name {
108+
api, _ := Load(currentConfig.Base, cmd)
109+
for _, op := range api.Operations {
110+
template := op.URITemplate
111+
if strings.HasPrefix(toComplete, currentConfig.name) {
112+
template = strings.Replace(template, currentConfig.Base, currentConfig.name, 1)
113+
}
114+
possible = append(possible, template)
115+
}
116+
}
117+
}
118+
return possible, cobra.ShellCompDirectiveNoFileComp
119+
}
120+
return []string{}, cobra.ShellCompDirectiveDefault
121+
}
122+
123+
func completeGenericCmd(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
124+
possible, directive := completeCurrentConfig(cmd, args, toComplete)
125+
if directive != cobra.ShellCompDirectiveDefault {
126+
return possible, directive
127+
}
128+
if currentConfig != nil {
129+
for _, cmd := range Root.Commands() {
130+
if cmd.Use == currentConfig.name {
131+
api, _ := Load(currentConfig.Base, cmd)
132+
for _, op := range api.Operations {
133+
template := op.URITemplate
134+
if strings.HasPrefix(toComplete, currentConfig.name) {
135+
template = strings.Replace(template, currentConfig.Base, currentConfig.name, 1)
136+
}
137+
possible = append(possible, template)
138+
}
139+
}
140+
}
141+
return possible, cobra.ShellCompDirectiveNoFileComp
142+
}
143+
105144
if len(args) == 0 {
106145
for name := range configs {
107146
possible = append(possible, name)
108147
}
109148
}
149+
110150
return possible, cobra.ShellCompDirectiveNoFileComp | cobra.ShellCompDirectiveNoSpace
111151
}
112152

@@ -152,7 +192,8 @@ func Init(name string, version string) {
152192
153193
# Specify verb, header, and body shorthand
154194
$ %s post :8888/users -H authorization:abc123 name: Kari, role: admin`, name, name),
155-
Args: cobra.MinimumNArgs(1),
195+
Args: cobra.MinimumNArgs(1),
196+
ValidArgsFunction: completeCurrentConfig,
156197
PersistentPreRun: func(cmd *cobra.Command, args []string) {
157198
settings := viper.AllSettings()
158199
LogDebug("Configuration: %v", settings)
@@ -168,7 +209,7 @@ func Init(name string, version string) {
168209
Short: "Head a URI",
169210
Long: "Perform an HTTP HEAD on the given URI",
170211
Args: cobra.MinimumNArgs(1),
171-
ValidArgsFunction: completeAPINames,
212+
ValidArgsFunction: completeGenericCmd,
172213
Run: func(cmd *cobra.Command, args []string) {
173214
generic(http.MethodHead, args[0], args[1:])
174215
},
@@ -180,7 +221,7 @@ func Init(name string, version string) {
180221
Short: "Options a URI",
181222
Long: "Perform an HTTP OPTIONS on the given URI",
182223
Args: cobra.MinimumNArgs(1),
183-
ValidArgsFunction: completeAPINames,
224+
ValidArgsFunction: completeGenericCmd,
184225
Run: func(cmd *cobra.Command, args []string) {
185226
generic(http.MethodOptions, args[0], args[1:])
186227
},
@@ -192,7 +233,7 @@ func Init(name string, version string) {
192233
Short: "Get a URI",
193234
Long: "Perform an HTTP GET on the given URI",
194235
Args: cobra.MinimumNArgs(1),
195-
ValidArgsFunction: completeAPINames,
236+
ValidArgsFunction: completeGenericCmd,
196237
Run: func(cmd *cobra.Command, args []string) {
197238
generic(http.MethodGet, args[0], args[1:])
198239
},
@@ -204,7 +245,7 @@ func Init(name string, version string) {
204245
Short: "Post a URI",
205246
Long: "Perform an HTTP POST on the given URI",
206247
Args: cobra.MinimumNArgs(1),
207-
ValidArgsFunction: completeAPINames,
248+
ValidArgsFunction: completeGenericCmd,
208249
Run: func(cmd *cobra.Command, args []string) {
209250
generic(http.MethodPost, args[0], args[1:])
210251
},
@@ -216,7 +257,7 @@ func Init(name string, version string) {
216257
Short: "Put a URI",
217258
Long: "Perform an HTTP PUT on the given URI",
218259
Args: cobra.MinimumNArgs(1),
219-
ValidArgsFunction: completeAPINames,
260+
ValidArgsFunction: completeGenericCmd,
220261
Run: func(cmd *cobra.Command, args []string) {
221262
generic(http.MethodPut, args[0], args[1:])
222263
},
@@ -228,7 +269,7 @@ func Init(name string, version string) {
228269
Short: "Patch a URI",
229270
Long: "Perform an HTTP PATCH on the given URI",
230271
Args: cobra.MinimumNArgs(1),
231-
ValidArgsFunction: completeAPINames,
272+
ValidArgsFunction: completeGenericCmd,
232273
Run: func(cmd *cobra.Command, args []string) {
233274
generic(http.MethodPatch, args[0], args[1:])
234275
},
@@ -240,7 +281,7 @@ func Init(name string, version string) {
240281
Short: "Delete a URI",
241282
Long: "Perform an HTTP DELETE on the given URI",
242283
Args: cobra.MinimumNArgs(1),
243-
ValidArgsFunction: completeAPINames,
284+
ValidArgsFunction: completeGenericCmd,
244285
Run: func(cmd *cobra.Command, args []string) {
245286
generic(http.MethodDelete, args[0], args[1:])
246287
},
@@ -255,7 +296,7 @@ func Init(name string, version string) {
255296
Short: "Edit a resource by URI",
256297
Long: "Convenience function which combines a GET, edit, and PUT operation into one command",
257298
Args: cobra.MinimumNArgs(1),
258-
ValidArgsFunction: completeAPINames,
299+
ValidArgsFunction: completeGenericCmd,
259300
Run: func(cmd *cobra.Command, args []string) {
260301
switch *editFormat {
261302
case "json":
@@ -285,7 +326,7 @@ func Init(name string, version string) {
285326
# Example usage with curl
286327
$ curl https://my-apiexample.com/ -H "Authorization: $(%s auth-header my-api)"`, name, name, name),
287328
Args: cobra.ExactArgs(1),
288-
ValidArgsFunction: completeAPINames,
329+
ValidArgsFunction: completeGenericCmd,
289330
RunE: func(cmd *cobra.Command, args []string) error {
290331
addr := fixAddress(args[0])
291332
name, config := findAPI(addr)
@@ -321,7 +362,7 @@ func Init(name string, version string) {
321362
Short: "Get cert info",
322363
Long: "Get TLS certificate information including expiration date",
323364
Args: cobra.ExactArgs(1),
324-
ValidArgsFunction: completeAPINames,
365+
ValidArgsFunction: completeGenericCmd,
325366
Run: func(cmd *cobra.Command, args []string) {
326367
u, err := url.Parse(fixAddress(args[0]))
327368
if err != nil {
@@ -373,7 +414,7 @@ Not after (expires): %s (%s)
373414
Short: "Get link relations from the given URI, with optional filtering",
374415
Long: "Returns a list of resolved references to the link relations after making an HTTP GET request to the given URI. Additional arguments filter down the set of returned relationship names.",
375416
Args: cobra.MinimumNArgs(1),
376-
ValidArgsFunction: completeAPINames,
417+
ValidArgsFunction: completeGenericCmd,
377418
Run: func(cmd *cobra.Command, args []string) {
378419
req, _ := http.NewRequest(http.MethodGet, fixAddress(args[0]), nil)
379420
resp, err := GetParsedResponse(req)
@@ -596,6 +637,7 @@ func Run() {
596637
apiName = args[2]
597638
}
598639

640+
loaded := false
599641
if apiName != "help" && apiName != "head" && apiName != "options" && apiName != "get" && apiName != "post" && apiName != "put" && apiName != "patch" && apiName != "delete" && apiName != "api" && apiName != "links" && apiName != "edit" && apiName != "auth-header" {
600642
// Try to find the registered config for this API. If not found,
601643
// there is no need to do anything since the normal flow will catch
@@ -607,11 +649,24 @@ func Run() {
607649
if _, err := Load(cfg.Base, cmd); err != nil {
608650
panic(err)
609651
}
652+
loaded = true
610653
break
611654
}
612655
}
613656
}
614657
}
658+
659+
if !loaded {
660+
// This could be a URL or short-name as part of a URL for generic
661+
// commands. We should load the config for shell completion.
662+
if apiName == "head" || apiName == "options" || apiName == "get" || apiName == "post" || apiName == "put" || apiName == "patch" || apiName == "delete" && len(args) > 2 {
663+
apiName = args[2]
664+
}
665+
apiName = fixAddress(apiName)
666+
if name, _ := findAPI(apiName); name != "" {
667+
currentConfig = configs[name]
668+
}
669+
}
615670
}
616671

617672
// Phew, we made it. Execute the command now that everything is loaded

0 commit comments

Comments
 (0)