|
| 1 | +// Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. |
| 2 | + |
1 | 3 | package provider |
2 | 4 |
|
3 | 5 | import ( |
| 6 | + "log" |
4 | 7 | "reflect" |
5 | 8 | "regexp" |
6 | | - |
7 | | - "fmt" |
| 9 | + "strconv" |
8 | 10 |
|
9 | 11 | "github.com/hashicorp/terraform/helper/schema" |
10 | 12 | ) |
@@ -49,72 +51,95 @@ func ApplyFilters(filters *schema.Set, items []map[string]interface{}) []map[str |
49 | 51 | keyword := fSet["name"].(string) |
50 | 52 |
|
51 | 53 | isReg := false |
52 | | - if regex, regexOk := fSet["regex"]; regexOk && regex != nil { |
53 | | - if strVal, strValOk := regex.(string); strValOk { |
54 | | - isReg = strVal == "1" || strVal == "true" |
55 | | - } else if boolVal, boolValOk := regex.(bool); boolValOk { |
56 | | - isReg = boolVal |
57 | | - } |
| 54 | + if regex, regexOk := fSet["regex"]; regexOk { |
| 55 | + isReg = regex.(bool) |
58 | 56 | } |
59 | 57 |
|
60 | | - check := func(filterVal string, propertyVal string) bool { |
| 58 | + // create a string equality check strategy based on this filters "regex" flag |
| 59 | + stringsEqual := func(propertyVal string, filterVal string) bool { |
61 | 60 | if isReg { |
62 | 61 | re, err := regexp.Compile(filterVal) |
63 | 62 | if err != nil { |
64 | | - panic(fmt.Errorf(`Invalid regular expression "%s" for "%s" filter`, filterVal, keyword)) |
| 63 | + // todo: when all SetData() fns are refactored to return a possible error, these log statements should |
| 64 | + // be converted to errors for return propagation |
| 65 | + log.Printf(`[WARN] Invalid regular expression "%s" for "%s" filter\n`, filterVal, keyword) |
| 66 | + return false |
65 | 67 | } |
66 | 68 | return re.MatchString(propertyVal) |
67 | 69 | } |
68 | 70 |
|
69 | 71 | return filterVal == propertyVal |
70 | 72 | } |
71 | 73 |
|
72 | | - orComparator := func(item map[string]interface{}) bool { |
73 | | - actualValue, valueExists := item[keyword] |
74 | | - if !valueExists { |
75 | | - return false |
| 74 | + // build a collection of items from matches against the set of filters |
| 75 | + res := make([]map[string]interface{}, 0) |
| 76 | + for _, item := range items { |
| 77 | + targetVal, targetValOk := item[keyword] |
| 78 | + if targetValOk && orComparator(targetVal, fSet["values"].([]interface{}), stringsEqual) { |
| 79 | + res = append(res, item) |
76 | 80 | } |
| 81 | + } |
| 82 | + items = res |
| 83 | + } |
77 | 84 |
|
78 | | - // We use reflection to determine whether the underlying type of the filtering attribute is a string or |
79 | | - // array of strings. Mainly used because the property could be an SDK enum with underlying string type. |
80 | | - // TODO: We should store SDK enum values in state as strings prior to calling ApplyFilters, to avoid using reflection |
81 | | - rValue := reflect.ValueOf(actualValue) |
82 | | - rType := rValue.Type() |
| 85 | + return items |
| 86 | +} |
83 | 87 |
|
84 | | - isStringArray := (rType.Kind() == reflect.Slice || rType.Kind() == reflect.Array) && rType.Elem().Kind() == reflect.String |
85 | | - isString := rType.Kind() == reflect.String |
86 | | - if !isStringArray && !isString { |
87 | | - // property is neither a string nor array of strings, so it can be filtered out |
| 88 | +type StringCheck func(propertyVal string, filterVal string) bool |
| 89 | + |
| 90 | +// orComparator returns true for any filter that matches the target property |
| 91 | +func orComparator(target interface{}, filters []interface{}, stringsEqual StringCheck) bool { |
| 92 | + // Use reflection to determine whether the underlying type of the filtering attribute is a string or |
| 93 | + // array of strings. Mainly used because the property could be an SDK enum with underlying string type. |
| 94 | + val := reflect.ValueOf(target) |
| 95 | + valType := val.Type() |
| 96 | + |
| 97 | + for _, fVal := range filters { |
| 98 | + switch valType.Kind() { |
| 99 | + case reflect.Bool: |
| 100 | + fBool, err := strconv.ParseBool(fVal.(string)) |
| 101 | + if err != nil { |
| 102 | + log.Println("[WARN] Filtering against Type Bool field with un-parsable string boolean form") |
88 | 103 | return false |
89 | 104 | } |
90 | | - |
91 | | - for _, filterValue := range fSet["values"].([]interface{}) { |
92 | | - if isStringArray { |
93 | | - arrLen := rValue.Len() |
94 | | - for i := 0; i < arrLen; i++ { |
95 | | - if check(filterValue.(string), rValue.Index(i).String()) { |
96 | | - return true |
97 | | - } |
| 105 | + if val.Bool() == fBool { |
| 106 | + return true |
| 107 | + } |
| 108 | + case reflect.Int: |
| 109 | + // the target field is of type int, but the filter values list element type is string, users can supply string |
| 110 | + // or int like `values = [300, "3600"]` but terraform will converts to string, so use ParseInt |
| 111 | + fInt, err := strconv.ParseInt(fVal.(string), 10, 64) |
| 112 | + if err != nil { |
| 113 | + log.Println("[WARN] Filtering against Type Int field with non-int filter value") |
| 114 | + return false |
| 115 | + } |
| 116 | + if val.Int() == fInt { |
| 117 | + return true |
| 118 | + } |
| 119 | + case reflect.Float64: |
| 120 | + // same comment as above for Ints |
| 121 | + fFloat, err := strconv.ParseFloat(fVal.(string), 64) |
| 122 | + if err != nil { |
| 123 | + log.Println("[WARN] Filtering against Type Float field with non-float filter value") |
| 124 | + return false |
| 125 | + } |
| 126 | + if val.Float() == fFloat { |
| 127 | + return true |
| 128 | + } |
| 129 | + case reflect.String: |
| 130 | + if stringsEqual(val.String(), fVal.(string)) { |
| 131 | + return true |
| 132 | + } |
| 133 | + case reflect.Slice, reflect.Array: |
| 134 | + if valType.Elem().Kind() == reflect.String { |
| 135 | + arrLen := val.Len() |
| 136 | + for i := 0; i < arrLen; i++ { |
| 137 | + if stringsEqual(val.Index(i).String(), fVal.(string)) { |
| 138 | + return true |
98 | 139 | } |
99 | | - } else if check(filterValue.(string), rValue.String()) { |
100 | | - return true |
101 | 140 | } |
102 | 141 | } |
103 | | - return false |
104 | | - } |
105 | | - |
106 | | - items = filter(items, orComparator) |
107 | | - } |
108 | | - |
109 | | - return items |
110 | | -} |
111 | | - |
112 | | -func filter(items []map[string]interface{}, comparator func(map[string]interface{}) bool) []map[string]interface{} { |
113 | | - res := make([]map[string]interface{}, 0) |
114 | | - for _, item := range items { |
115 | | - if comparator(item) { |
116 | | - res = append(res, item) |
117 | 142 | } |
118 | 143 | } |
119 | | - return res |
| 144 | + return false |
120 | 145 | } |
0 commit comments