github.com/avenga/couper@v1.12.2/internal/seetie/convert.go (about) 1 package seetie 2 3 import ( 4 "fmt" 5 "math/big" 6 "net/http" 7 "net/url" 8 "regexp" 9 "strconv" 10 "strings" 11 12 "github.com/sirupsen/logrus" 13 "github.com/zclconf/go-cty/cty" 14 ) 15 16 var validKey = regexp.MustCompile("[a-zA-Z_][a-zA-Z0-9_-]*") 17 18 func ValueToMap(val cty.Value) map[string]interface{} { 19 result := make(map[string]interface{}) 20 if val.IsNull() || !val.IsKnown() { 21 return result 22 } 23 var valMap map[string]cty.Value 24 if isListOrTuple(val) { 25 valMap = val.AsValueSlice()[0].AsValueMap() 26 } else { 27 valMap = val.AsValueMap() 28 } 29 30 for k, v := range valMap { 31 if v.IsNull() || !v.IsWhollyKnown() { 32 result[k] = nil 33 continue 34 } 35 t := v.Type() 36 switch t { 37 case cty.Bool: 38 result[k] = v.True() 39 case cty.String: 40 result[k] = v.AsString() 41 case cty.List(cty.String): 42 result[k] = ValueToStringSlice(v) 43 case cty.Number: 44 f, _ := v.AsBigFloat().Float64() 45 result[k] = f 46 case cty.Map(cty.NilType): 47 result[k] = nil 48 default: 49 if t.IsObjectType() { 50 result[k] = ValueToMap(v) 51 continue 52 } 53 if isListOrTuple(v) { 54 result[k] = ValueToStringSlice(v) 55 continue 56 } 57 result[k] = nil 58 } 59 } 60 return result 61 } 62 63 func ValueToPermission(val cty.Value) (string, map[string]string, error) { 64 switch val.Type() { 65 case cty.NilType: 66 return "", nil, nil 67 case cty.String: 68 return val.AsString(), nil, nil 69 default: 70 if val.Type().IsObjectType() { 71 permissionMap := make(map[string]string) 72 for k, v := range val.AsValueMap() { 73 if v.Type() != cty.String { 74 return "", nil, fmt.Errorf("unsupported value for method %q in required_permission", k) 75 } 76 permissionMap[strings.ToUpper(k)] = v.AsString() 77 } 78 return "", permissionMap, nil 79 } 80 } 81 return "", nil, fmt.Errorf("unsupported value for required_permission") 82 } 83 84 func ValuesMapToValue(m url.Values) cty.Value { 85 result := make(map[string]interface{}) 86 for k, v := range m { 87 result[k] = v 88 } 89 return MapToValue(result) 90 } 91 92 func stringListToValue(l []string) cty.Value { 93 if len(l) == 0 { 94 return cty.ListValEmpty(cty.String) 95 } 96 97 var list []cty.Value 98 for _, s := range l { 99 list = append(list, cty.StringVal(s)) 100 } 101 return cty.ListVal(list) 102 } 103 104 func listToValue(l []interface{}) cty.Value { 105 var list []cty.Value 106 for _, v := range l { 107 list = append(list, GoToValue(v)) 108 } 109 return cty.TupleVal(list) 110 } 111 112 func GoToValue(v interface{}) cty.Value { 113 switch v := v.(type) { 114 case string: 115 return cty.StringVal(v) 116 case bool: 117 return cty.BoolVal(v) 118 case int64: 119 return cty.NumberIntVal(v) 120 case float64: 121 return cty.NumberFloatVal(v) 122 case []string: 123 return stringListToValue(v) 124 case []interface{}: 125 return listToValue(v) 126 case map[string]interface{}: 127 return MapToValue(v) 128 default: 129 return cty.NullVal(cty.String) 130 } 131 } 132 133 func MapToValue(m map[string]interface{}) cty.Value { 134 if m == nil { 135 return cty.MapValEmpty(cty.NilType) 136 } 137 138 ctyMap := make(map[string]cty.Value) 139 140 for k, v := range m { 141 if !validKey.MatchString(k) { 142 continue 143 } 144 switch v := v.(type) { 145 case []string: 146 ctyMap[k] = stringListToValue(v) 147 case []interface{}: 148 ctyMap[k] = listToValue(v) 149 case map[string]interface{}: 150 ctyMap[k] = MapToValue(v) 151 default: 152 ctyMap[k] = GoToValue(v) 153 } 154 } 155 156 if len(ctyMap) == 0 { 157 return cty.MapValEmpty(cty.NilType) // prevent attribute access on nil values 158 } 159 160 return cty.ObjectVal(ctyMap) 161 } 162 163 func HeaderToMapValue(headers http.Header) cty.Value { 164 ctyMap := make(map[string]cty.Value) 165 for k, v := range headers { 166 if validKey.MatchString(k) { 167 if len(v) == 0 { 168 ctyMap[strings.ToLower(k)] = cty.StringVal("") 169 continue 170 } 171 ctyMap[strings.ToLower(k)] = cty.StringVal(v[0]) // TODO: ListVal?? 172 } 173 } 174 if len(ctyMap) == 0 { 175 return cty.MapValEmpty(cty.String) 176 } 177 return cty.MapVal(ctyMap) 178 } 179 180 func CookiesToMapValue(cookies []*http.Cookie) cty.Value { 181 ctyMap := make(map[string]cty.Value) 182 for _, cookie := range cookies { 183 ctyMap[cookie.Name] = cty.StringVal(cookie.Value) // TODO: ListVal?? 184 } 185 186 if len(ctyMap) == 0 { 187 return cty.MapValEmpty(cty.String) 188 } 189 return cty.MapVal(ctyMap) 190 } 191 192 func ValueToStringSlice(src cty.Value) []string { 193 var l []string 194 if !src.IsKnown() || src.IsNull() { 195 return l 196 } 197 198 switch src.Type() { 199 case cty.NilType: 200 return l 201 case cty.Bool, cty.Number, cty.String: 202 return append(l, ValueToString(src)) 203 default: 204 for _, s := range src.AsValueSlice() { 205 if !s.IsKnown() { 206 continue 207 } 208 l = append(l, ValueToString(s)) 209 } 210 } 211 return l 212 } 213 214 func ValueToIntSlice(src cty.Value) []int64 { 215 var n []int64 216 if !src.IsKnown() || src.IsNull() || !src.CanIterateElements() { 217 return n 218 } 219 220 for _, s := range src.AsValueSlice() { 221 if !s.IsKnown() { 222 continue 223 } 224 n = append(n, ValueToInt(s)) 225 } 226 return n 227 } 228 229 var whitespaceRegex = regexp.MustCompile(`^\s*$`) 230 231 // ValueToString explicitly drops all other (unknown) types and 232 // converts non whitespace strings or numbers to its string representation. 233 func ValueToString(v cty.Value) string { 234 if v.IsNull() || !v.IsKnown() { 235 return "" 236 } 237 238 switch v.Type() { 239 case cty.String: 240 str := v.AsString() 241 if whitespaceRegex.MatchString(str) { 242 return "" 243 } 244 return str 245 case cty.Number: 246 n := v.AsBigFloat() 247 ni, accuracy := n.Int(nil) 248 if accuracy == big.Exact { 249 return ni.String() 250 } 251 return n.String() 252 default: 253 return "" 254 } 255 } 256 257 func ValueToInt(v cty.Value) (n int64) { 258 if !v.IsWhollyKnown() { 259 return n 260 } 261 262 switch v.Type() { 263 case cty.String: 264 i, err := strconv.Atoi(v.AsString()) 265 if err == nil { 266 n = int64(i) 267 } 268 case cty.Number: 269 bn := v.AsBigFloat() 270 n, _ = bn.Int64() 271 } 272 273 return n 274 } 275 276 func ValueToLogFields(val cty.Value) logrus.Fields { 277 if val.IsNull() || !val.IsKnown() { 278 return nil 279 } 280 281 fields := logrus.Fields{} 282 283 for k, v := range val.AsValueMap() { 284 if isListOrTuple(v) { 285 fields[k] = valueToLogFieldsFromListOrTuple(v) 286 } else { 287 switch v.Type() { 288 case cty.Bool: 289 fields[k] = v.True() 290 case cty.String: 291 fields[k] = v.AsString() 292 case cty.Number: 293 f, _ := v.AsBigFloat().Float64() 294 fields[k] = f 295 default: 296 if isMapOrObject(v) { 297 fields[k] = ValueToLogFields(v) 298 } 299 } 300 } 301 } 302 303 return fields 304 } 305 306 func valueToLogFieldsFromListOrTuple(val cty.Value) []interface{} { 307 if !isListOrTuple(val) { 308 return nil 309 } 310 311 var values []interface{} 312 for _, v := range val.AsValueSlice() { 313 if isListOrTuple(v) { 314 values = append(values, valueToLogFieldsFromListOrTuple(v)) 315 } else { 316 switch v.Type() { 317 case cty.Bool: 318 values = append(values, v.True()) 319 case cty.String: 320 values = append(values, v.AsString()) 321 case cty.Number: 322 f, _ := v.AsBigFloat().Float64() 323 values = append(values, f) 324 default: 325 if isMapOrObject(v) { 326 values = append(values, ValueToLogFields(v)) 327 } 328 } 329 } 330 } 331 332 return values 333 } 334 335 func SliceToString(sl []interface{}) string { 336 var str []string 337 for _, s := range sl { 338 if result := ToString(s); result != "" { 339 str = append(str, result) 340 } 341 } 342 return strings.Join(str, ",") 343 } 344 345 func ToString(s interface{}) string { 346 switch s := s.(type) { 347 case []string: 348 return strings.Join(s, ",") 349 case []interface{}: 350 return SliceToString(s) 351 case string: 352 return s 353 case int: 354 return strconv.Itoa(s) 355 case float64: 356 return fmt.Sprintf("%0.f", s) 357 case bool: 358 if !s { 359 return "false" 360 } 361 return "true" 362 default: 363 return "" 364 } 365 } 366 367 func isMapOrObject(v cty.Value) bool { 368 if v.IsNull() { 369 return false 370 } 371 t := v.Type() 372 return t.IsMapType() || t.IsObjectType() 373 } 374 375 func isListOrTuple(v cty.Value) bool { 376 if v.IsNull() { 377 return false 378 } 379 t := v.Type() 380 return t.IsListType() || t.IsTupleType() 381 }