Skip to content

Commit bdfbeb3

Browse files
committed
Add support for excluding a list of parameters from an API response
1 parent 91f5600 commit bdfbeb3

File tree

4 files changed

+82
-26
lines changed

4 files changed

+82
-26
lines changed

cli/completer.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ func (t *autoCompleter) Do(line []rune, pos int) (options [][]rune, offset int)
357357
}
358358
return
359359
}
360+
360361
if arg.Type == config.FAKE && arg.Name == "filter=" {
361362
offset = 0
362363
filterInputs := strings.Split(strings.Replace(argInput, ",", ",|", -1), "|")
@@ -373,6 +374,22 @@ func (t *autoCompleter) Do(line []rune, pos int) (options [][]rune, offset int)
373374
return
374375
}
375376

377+
if arg.Type == config.FAKE && arg.Name == "exclude=" {
378+
offset = 0
379+
excludeFilterInputs := strings.Split(strings.Replace(argInput, ",", ",|", -1), "|")
380+
lastExcludeFilterInput := lastString(excludeFilterInputs)
381+
for _, key := range apiFound.ResponseKeys {
382+
if inArray(key, excludeFilterInputs) {
383+
continue
384+
}
385+
if strings.HasPrefix(key, lastExcludeFilterInput) {
386+
options = append(options, []rune(key[len(lastExcludeFilterInput):]))
387+
offset = len(lastExcludeFilterInput)
388+
}
389+
}
390+
return
391+
}
392+
376393
autocompleteAPI := findAutocompleteAPI(arg, apiFound, apiMap)
377394
if autocompleteAPI == nil {
378395
return nil, 0

cmd/api.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func init() {
8282
if strings.HasSuffix(err.Error(), "context canceled") {
8383
return nil
8484
} else if response != nil {
85-
printResult(r.Config.Core.Output, response, nil)
85+
printResult(r.Config.Core.Output, response, nil, nil)
8686
}
8787
return err
8888
}
@@ -98,8 +98,19 @@ func init() {
9898
}
9999
}
100100

101+
var excludeKeys []string
102+
for _, arg := range apiArgs {
103+
if strings.HasPrefix(arg, "exclude=") {
104+
for _, excludeKey := range strings.Split(strings.Split(arg, "=")[1], ",") {
105+
if len(strings.TrimSpace(excludeKey)) > 0 {
106+
excludeKeys = append(excludeKeys, strings.TrimSpace(excludeKey))
107+
}
108+
}
109+
}
110+
}
111+
101112
if len(response) > 0 {
102-
printResult(r.Config.Core.Output, response, filterKeys)
113+
printResult(r.Config.Core.Output, response, filterKeys, excludeKeys)
103114
}
104115

105116
return nil

cmd/output.go

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -206,51 +206,72 @@ func printCsv(response map[string]interface{}, filter []string) {
206206
enc.Flush()
207207
}
208208

209-
func filterResponse(response map[string]interface{}, filter []string, outputType string) map[string]interface{} {
210-
if filter == nil || len(filter) == 0 {
209+
func filterResponse(response map[string]interface{}, filter []string, excludeFilter []string, outputType string) map[string]interface{} {
210+
// If no filtering is required, return the original response.
211+
if len(filter) == 0 && len(excludeFilter) == 0 {
211212
return response
212213
}
214+
215+
// Create a set for excludeFilter to improve lookup performance.
216+
excludeSet := make(map[string]struct{}, len(excludeFilter))
217+
for _, key := range excludeFilter {
218+
excludeSet[key] = struct{}{}
219+
}
220+
221+
// Create a set for filter keys for fast lookup.
222+
filterSet := make(map[string]struct{}, len(filter))
223+
for _, key := range filter {
224+
filterSet[key] = struct{}{}
225+
}
226+
213227
filteredResponse := make(map[string]interface{})
214-
for k, v := range response {
215-
valueType := reflect.TypeOf(v)
216-
if valueType.Kind() == reflect.Slice || valueType.Kind() == reflect.Map {
217-
items, ok := v.([]interface{})
218-
if !ok {
219-
continue
220-
}
228+
229+
for key, value := range response {
230+
// Process only slice/map values
231+
switch items := value.(type) {
232+
case []interface{}:
221233
var filteredRows []interface{}
222234
for _, item := range items {
223235
row, ok := item.(map[string]interface{})
224-
if !ok || len(row) < 1 {
236+
if !ok || len(row) == 0 {
225237
continue
226238
}
239+
227240
filteredRow := make(map[string]interface{})
228-
for _, filterKey := range filter {
229-
for field := range row {
230-
if filterKey == field {
231-
filteredRow[field] = row[field]
241+
242+
if len(filter) > 0 {
243+
// Include only keys that exist in filterSet
244+
for filterKey := range filterSet {
245+
if val, exists := row[filterKey]; exists {
246+
filteredRow[filterKey] = val
247+
} else if outputType == config.COLUMN || outputType == config.CSV || outputType == config.TABLE {
248+
filteredRow[filterKey] = "" // Ensure all filter keys exist in row
232249
}
233250
}
234-
if outputType == config.COLUMN || outputType == config.CSV || outputType == config.TABLE {
235-
if _, ok := filteredRow[filterKey]; !ok {
236-
filteredRow[filterKey] = ""
251+
} else {
252+
// Exclude keys from excludeFilter
253+
for field, val := range row {
254+
if _, excluded := excludeSet[field]; !excluded {
255+
filteredRow[field] = val
237256
}
238257
}
239258
}
259+
240260
filteredRows = append(filteredRows, filteredRow)
241261
}
242-
filteredResponse[k] = filteredRows
243-
} else {
244-
filteredResponse[k] = v
245-
continue
246-
}
262+
filteredResponse[key] = filteredRows
247263

264+
default:
265+
// If it's not a slice, copy as is
266+
filteredResponse[key] = value
267+
}
248268
}
269+
249270
return filteredResponse
250271
}
251272

252-
func printResult(outputType string, response map[string]interface{}, filter []string) {
253-
response = filterResponse(response, filter, outputType)
273+
func printResult(outputType string, response map[string]interface{}, filter []string, excludeFilter []string) {
274+
response = filterResponse(response, filter, excludeFilter, outputType)
254275
switch outputType {
255276
case config.JSON:
256277
printJSON(response)

config/cache.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,13 @@ func (c *Config) UpdateCache(response map[string]interface{}) interface{} {
151151
Description: "cloudmonkey specific response key filtering",
152152
})
153153

154+
// Add exclude arg
155+
apiArgs = append(apiArgs, &APIArg{
156+
Name: "exclude=",
157+
Type: FAKE,
158+
Description: "cloudmonkey specific response key to exlude when filtering",
159+
})
160+
154161
sort.Slice(apiArgs, func(i, j int) bool {
155162
return apiArgs[i].Name < apiArgs[j].Name
156163
})

0 commit comments

Comments
 (0)