You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by pe...@apache.org on 2025/04/03 13:23:35 UTC
(cloudstack-cloudmonkey) 01/01: Add support for excluding a list of parameters from an API response
This is an automated email from the ASF dual-hosted git repository.
pearl11594 pushed a commit to branch support-response-exlude-list-filtering
in repository https://gitbox.apache.org/repos/asf/cloudstack-cloudmonkey.git
commit bdfbeb36c50025e3a560683e3eabafd6ec54dca7
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Thu Apr 3 09:23:20 2025 -0400
Add support for excluding a list of parameters from an API response
---
cli/completer.go | 17 ++++++++++++++
cmd/api.go | 15 ++++++++++--
cmd/output.go | 69 ++++++++++++++++++++++++++++++++++++--------------------
config/cache.go | 7 ++++++
4 files changed, 82 insertions(+), 26 deletions(-)
diff --git a/cli/completer.go b/cli/completer.go
index be1ba21..53de5fb 100644
--- a/cli/completer.go
+++ b/cli/completer.go
@@ -357,6 +357,7 @@ func (t *autoCompleter) Do(line []rune, pos int) (options [][]rune, offset int)
}
return
}
+
if arg.Type == config.FAKE && arg.Name == "filter=" {
offset = 0
filterInputs := strings.Split(strings.Replace(argInput, ",", ",|", -1), "|")
@@ -373,6 +374,22 @@ func (t *autoCompleter) Do(line []rune, pos int) (options [][]rune, offset int)
return
}
+ if arg.Type == config.FAKE && arg.Name == "exclude=" {
+ offset = 0
+ excludeFilterInputs := strings.Split(strings.Replace(argInput, ",", ",|", -1), "|")
+ lastExcludeFilterInput := lastString(excludeFilterInputs)
+ for _, key := range apiFound.ResponseKeys {
+ if inArray(key, excludeFilterInputs) {
+ continue
+ }
+ if strings.HasPrefix(key, lastExcludeFilterInput) {
+ options = append(options, []rune(key[len(lastExcludeFilterInput):]))
+ offset = len(lastExcludeFilterInput)
+ }
+ }
+ return
+ }
+
autocompleteAPI := findAutocompleteAPI(arg, apiFound, apiMap)
if autocompleteAPI == nil {
return nil, 0
diff --git a/cmd/api.go b/cmd/api.go
index 058c6fb..872a328 100644
--- a/cmd/api.go
+++ b/cmd/api.go
@@ -82,7 +82,7 @@ func init() {
if strings.HasSuffix(err.Error(), "context canceled") {
return nil
} else if response != nil {
- printResult(r.Config.Core.Output, response, nil)
+ printResult(r.Config.Core.Output, response, nil, nil)
}
return err
}
@@ -98,8 +98,19 @@ func init() {
}
}
+ var excludeKeys []string
+ for _, arg := range apiArgs {
+ if strings.HasPrefix(arg, "exclude=") {
+ for _, excludeKey := range strings.Split(strings.Split(arg, "=")[1], ",") {
+ if len(strings.TrimSpace(excludeKey)) > 0 {
+ excludeKeys = append(excludeKeys, strings.TrimSpace(excludeKey))
+ }
+ }
+ }
+ }
+
if len(response) > 0 {
- printResult(r.Config.Core.Output, response, filterKeys)
+ printResult(r.Config.Core.Output, response, filterKeys, excludeKeys)
}
return nil
diff --git a/cmd/output.go b/cmd/output.go
index 08004cf..f45e6d8 100644
--- a/cmd/output.go
+++ b/cmd/output.go
@@ -206,51 +206,72 @@ func printCsv(response map[string]interface{}, filter []string) {
enc.Flush()
}
-func filterResponse(response map[string]interface{}, filter []string, outputType string) map[string]interface{} {
- if filter == nil || len(filter) == 0 {
+func filterResponse(response map[string]interface{}, filter []string, excludeFilter []string, outputType string) map[string]interface{} {
+ // If no filtering is required, return the original response.
+ if len(filter) == 0 && len(excludeFilter) == 0 {
return response
}
+
+ // Create a set for excludeFilter to improve lookup performance.
+ excludeSet := make(map[string]struct{}, len(excludeFilter))
+ for _, key := range excludeFilter {
+ excludeSet[key] = struct{}{}
+ }
+
+ // Create a set for filter keys for fast lookup.
+ filterSet := make(map[string]struct{}, len(filter))
+ for _, key := range filter {
+ filterSet[key] = struct{}{}
+ }
+
filteredResponse := make(map[string]interface{})
- for k, v := range response {
- valueType := reflect.TypeOf(v)
- if valueType.Kind() == reflect.Slice || valueType.Kind() == reflect.Map {
- items, ok := v.([]interface{})
- if !ok {
- continue
- }
+
+ for key, value := range response {
+ // Process only slice/map values
+ switch items := value.(type) {
+ case []interface{}:
var filteredRows []interface{}
for _, item := range items {
row, ok := item.(map[string]interface{})
- if !ok || len(row) < 1 {
+ if !ok || len(row) == 0 {
continue
}
+
filteredRow := make(map[string]interface{})
- for _, filterKey := range filter {
- for field := range row {
- if filterKey == field {
- filteredRow[field] = row[field]
+
+ if len(filter) > 0 {
+ // Include only keys that exist in filterSet
+ for filterKey := range filterSet {
+ if val, exists := row[filterKey]; exists {
+ filteredRow[filterKey] = val
+ } else if outputType == config.COLUMN || outputType == config.CSV || outputType == config.TABLE {
+ filteredRow[filterKey] = "" // Ensure all filter keys exist in row
}
}
- if outputType == config.COLUMN || outputType == config.CSV || outputType == config.TABLE {
- if _, ok := filteredRow[filterKey]; !ok {
- filteredRow[filterKey] = ""
+ } else {
+ // Exclude keys from excludeFilter
+ for field, val := range row {
+ if _, excluded := excludeSet[field]; !excluded {
+ filteredRow[field] = val
}
}
}
+
filteredRows = append(filteredRows, filteredRow)
}
- filteredResponse[k] = filteredRows
- } else {
- filteredResponse[k] = v
- continue
- }
+ filteredResponse[key] = filteredRows
+ default:
+ // If it's not a slice, copy as is
+ filteredResponse[key] = value
+ }
}
+
return filteredResponse
}
-func printResult(outputType string, response map[string]interface{}, filter []string) {
- response = filterResponse(response, filter, outputType)
+func printResult(outputType string, response map[string]interface{}, filter []string, excludeFilter []string) {
+ response = filterResponse(response, filter, excludeFilter, outputType)
switch outputType {
case config.JSON:
printJSON(response)
diff --git a/config/cache.go b/config/cache.go
index 096582d..13596dd 100644
--- a/config/cache.go
+++ b/config/cache.go
@@ -151,6 +151,13 @@ func (c *Config) UpdateCache(response map[string]interface{}) interface{} {
Description: "cloudmonkey specific response key filtering",
})
+ // Add exclude arg
+ apiArgs = append(apiArgs, &APIArg{
+ Name: "exclude=",
+ Type: FAKE,
+ Description: "cloudmonkey specific response key to exlude when filtering",
+ })
+
sort.Slice(apiArgs, func(i, j int) bool {
return apiArgs[i].Name < apiArgs[j].Name
})