github.com/vmware/go-vcloud-director/v2@v2.24.0/govcd/filter_util.go (about) 1 package govcd 2 3 /* 4 * Copyright 2020 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. 5 */ 6 7 import ( 8 "fmt" 9 "regexp" 10 "strings" 11 12 "github.com/araddon/dateparse" 13 14 "github.com/vmware/go-vcloud-director/v2/types/v56" 15 ) 16 17 var ( 18 // supportedFilters lists the filters currently supported in the engine, available to users 19 supportedFilters = []string{ 20 types.FilterNameRegex, 21 types.FilterDate, 22 types.FilterIp, 23 types.FilterLatest, 24 types.FilterEarliest, 25 types.FilterParent, 26 types.FilterParentId, 27 } 28 29 // SupportedMetadataTypes are the metadata types recognized so far. "NONE" is the same as "" 30 SupportedMetadataTypes = []string{"NONE", "STRING", "NUMBER", "BOOLEAN", "DATETIME"} 31 ) 32 33 // MetadataDef defines a metadata structure 34 type MetadataDef struct { 35 Key string // name of the field (addressed as metadata:key) 36 Type string // Type of the field (one of SupportedMetadataTypes) 37 Value interface{} // contents of the metadata field 38 IsSystem bool // if true, the metadata field will be addressed as metadata@SYSTEM:key 39 } 40 41 // matchResult stores the result of a condition evaluation 42 // Used to build the human readable description of the engine operations 43 type matchResult struct { 44 Name string 45 Type string 46 Definition string 47 Result bool 48 } 49 50 // FilterDef defines all the criteria used by the engine to retrieve data 51 type FilterDef struct { 52 // A collection of filters (with keys from SupportedFilters) 53 Filters map[string]string 54 55 // A list of metadata filters 56 Metadata []MetadataDef 57 58 // If true, the query will include metadata fields and search for exact values. 59 // Otherwise, the engine will collect metadata fields and search by regexp 60 UseMetadataApiFilter bool 61 } 62 63 // NewFilterDef builds a new filter definition 64 func NewFilterDef() *FilterDef { 65 return &FilterDef{ 66 Filters: make(map[string]string), 67 Metadata: nil, 68 } 69 } 70 71 // validateMetadataType checks that a metadata type is within supported types 72 func validateMetadataType(valueType string) error { 73 typeSupported := false 74 for _, supported := range SupportedMetadataTypes { 75 if valueType == supported { 76 typeSupported = true 77 } 78 } 79 if !typeSupported { 80 return fmt.Errorf("metadata type '%s' not supported", valueType) 81 } 82 return nil 83 } 84 85 // AddFilter adds a new filter to the criteria 86 func (fd *FilterDef) AddFilter(key, value string) error { 87 for _, allowed := range supportedFilters { 88 if key == allowed { 89 fd.Filters[key] = value 90 return nil 91 } 92 } 93 return fmt.Errorf("filter '%s' not supported", key) 94 } 95 96 // AddMetadataFilter adds a new metadata filter to an existing set 97 func (fd *FilterDef) AddMetadataFilter(key, value, valueType string, isSystem, useMetadataApiFilter bool) error { 98 if valueType == "" { 99 valueType = "NONE" 100 useMetadataApiFilter = false 101 } 102 if useMetadataApiFilter { 103 fd.UseMetadataApiFilter = true 104 } 105 err := validateMetadataType(valueType) 106 if err != nil { 107 return err 108 } 109 fd.Metadata = append(fd.Metadata, MetadataDef{ 110 Key: key, 111 Value: value, 112 IsSystem: isSystem, 113 Type: valueType, 114 }) 115 return nil 116 } 117 118 // stringToBool converts a string to a bool 119 // The following values are recognized as TRUE: 120 // 121 // t, true, y, yes, ok 122 func stringToBool(s string) bool { 123 switch strings.ToLower(s) { 124 case "t", "true", "y", "yes", "ok": 125 return true 126 default: 127 return false 128 } 129 } 130 131 // compareDate will get a date from string `got`, and will parse `wanted` 132 // for an expression starting with an operator (>, <, >=, <=, ==) followed by a date 133 // (many formats supported, but 'YYYY-MM-DD[ hh:mm[:ss[.nnnZ]]' preferred) 134 // For example: 135 // got: "2020-03-09T09:50:51.500Z" 136 // wanted: ">= 2020-03-08" 137 // result: true 138 // got: "2020-03-09T09:50:51.500Z" 139 // wanted: "< 02-mar-2020" 140 // result: false 141 // See https://github.com/araddon/dateparse for more info 142 func compareDate(wanted, got string) (bool, error) { 143 144 reExpression := regexp.MustCompile(`(>=|<=|==|<|=|>)\s*(.+)`) 145 146 expList := reExpression.FindAllStringSubmatch(wanted, -1) 147 if len(expList) == 0 || len(expList[0]) == 0 { 148 return false, fmt.Errorf("expression not found in '%s'", wanted) 149 } 150 151 operator := expList[0][1] 152 wantedTime, err := dateparse.ParseStrict(expList[0][2]) 153 if err != nil { 154 return false, err 155 } 156 157 gotTime, err := dateparse.ParseStrict(got) 158 if err != nil { 159 return false, err 160 } 161 162 wantedSeconds := wantedTime.UnixNano() 163 gotSeconds := gotTime.UnixNano() 164 165 switch operator { 166 case "=", "==": 167 return gotSeconds == wantedSeconds, nil 168 case ">": 169 return gotSeconds > wantedSeconds, nil 170 case ">=": 171 return gotSeconds >= wantedSeconds, nil 172 case "<=": 173 return gotSeconds <= wantedSeconds, nil 174 case "<": 175 return gotSeconds < wantedSeconds, nil 176 default: 177 return false, fmt.Errorf("unsupported operator '%s'", operator) 178 } 179 } 180 181 // conditionText provides a human readable string of searching criteria 182 func conditionText(criteria *FilterDef) string { 183 result := "criteria: " 184 185 for k, v := range criteria.Filters { 186 result += fmt.Sprintf(`("%s" -> "%s") `, k, v) 187 } 188 for _, m := range criteria.Metadata { 189 marker := "metadata" 190 if criteria.UseMetadataApiFilter { 191 marker = "metadataApi" 192 } 193 result += fmt.Sprintf(`%s("%s" -> "%s") `, marker, m.Key, m.Value) 194 } 195 return result 196 } 197 198 // matchesToText provides a human readable string of search operations results 199 func matchesToText(matches []matchResult) string { 200 result := "" 201 for _, item := range matches { 202 result += fmt.Sprintf("name: %s; type: %s definition: %s; result: %v\n", item.Name, item.Type, item.Definition, item.Result) 203 } 204 return result 205 }