github.com/moleculer-go/moleculer@v0.3.3/payload/payload.go (about) 1 package payload 2 3 import ( 4 "errors" 5 "fmt" 6 "regexp" 7 "sort" 8 "strconv" 9 "strings" 10 "time" 11 12 "go.mongodb.org/mongo-driver/bson" 13 14 "github.com/moleculer-go/moleculer" 15 ) 16 17 // RawPayload is a payload implementation for raw types. 18 type RawPayload struct { 19 source interface{} 20 } 21 22 func (p *RawPayload) Exists() bool { 23 return p.source != nil 24 } 25 26 func (p *RawPayload) IsError() bool { 27 _, isError := p.source.(error) 28 _, isPError := p.source.(payloadError) 29 return isError || isPError 30 } 31 32 func (p *RawPayload) Error() error { 33 if p.IsError() { 34 return p.source.(error) 35 } 36 return nil 37 } 38 39 func (p *RawPayload) Int() int { 40 value, ok := p.source.(int) 41 if !ok { 42 if transformer := getNumberTransformer(&p.source); transformer != nil { 43 value = transformer.toInt(&p.source) 44 } 45 } 46 return value 47 } 48 49 func (p *RawPayload) Int64() int64 { 50 value, ok := p.source.(int64) 51 if !ok { 52 if transformer := getNumberTransformer(&p.source); transformer != nil { 53 value = transformer.toInt64(&p.source) 54 } 55 } 56 return value 57 } 58 59 func (p *RawPayload) Bool() bool { 60 value, ok := p.source.(bool) 61 if !ok { 62 value = strings.ToLower(fmt.Sprint(p.source)) == "true" 63 } 64 return value 65 } 66 67 func (p *RawPayload) Uint() uint64 { 68 value, ok := p.source.(uint64) 69 if !ok { 70 if transformer := getNumberTransformer(&p.source); transformer != nil { 71 value = transformer.toUint64(&p.source) 72 } 73 } 74 return value 75 } 76 77 func (p *RawPayload) Time() time.Time { 78 return p.source.(time.Time) 79 } 80 81 func (p *RawPayload) StringArray() []string { 82 if source := p.Array(); source != nil { 83 array := make([]string, len(source)) 84 for index, item := range source { 85 array[index] = item.String() 86 } 87 return array 88 } 89 return nil 90 } 91 92 func (p *RawPayload) MapArray() []map[string]interface{} { 93 if source := p.Array(); source != nil { 94 array := make([]map[string]interface{}, len(source)) 95 for index, item := range source { 96 array[index] = item.RawMap() 97 } 98 return array 99 } 100 return nil 101 } 102 103 func (p *RawPayload) ValueArray() []interface{} { 104 if source := p.Array(); source != nil { 105 array := make([]interface{}, len(source)) 106 for index, item := range source { 107 array[index] = item.Value() 108 } 109 return array 110 } 111 return nil 112 } 113 114 func (p *RawPayload) IntArray() []int { 115 if source := p.Array(); source != nil { 116 array := make([]int, len(source)) 117 for index, item := range source { 118 array[index] = item.Int() 119 } 120 return array 121 } 122 return nil 123 } 124 125 func (p *RawPayload) Int64Array() []int64 { 126 if source := p.Array(); source != nil { 127 array := make([]int64, len(source)) 128 for index, item := range source { 129 array[index] = item.Int64() 130 } 131 return array 132 } 133 return nil 134 } 135 136 func (p *RawPayload) UintArray() []uint64 { 137 if source := p.Array(); source != nil { 138 array := make([]uint64, len(source)) 139 for index, item := range source { 140 array[index] = item.Uint() 141 } 142 return array 143 } 144 return nil 145 } 146 147 func (p *RawPayload) Float32Array() []float32 { 148 if source := p.Array(); source != nil { 149 array := make([]float32, len(source)) 150 for index, item := range source { 151 array[index] = item.Float32() 152 } 153 return array 154 } 155 return nil 156 } 157 158 func (p *RawPayload) FloatArray() []float64 { 159 if source := p.Array(); source != nil { 160 array := make([]float64, len(source)) 161 for index, item := range source { 162 array[index] = item.Float() 163 } 164 return array 165 } 166 return nil 167 } 168 169 func (p *RawPayload) BoolArray() []bool { 170 ba, ok := p.source.([]bool) 171 if ok { 172 return ba 173 } 174 if source := p.Array(); source != nil { 175 array := make([]bool, len(source)) 176 for index, item := range source { 177 array[index] = item.Bool() 178 } 179 return array 180 } 181 return nil 182 } 183 184 func (p *RawPayload) ByteArray() []byte { 185 ba, ok := p.source.([]byte) 186 if ok { 187 return ba 188 } 189 return nil 190 } 191 192 func (p *RawPayload) TimeArray() []time.Time { 193 if source := p.Array(); source != nil { 194 array := make([]time.Time, len(source)) 195 for index, item := range source { 196 array[index] = item.Time() 197 } 198 return array 199 } 200 return nil 201 } 202 203 func (p *RawPayload) Len() int { 204 if transformer := ArrayTransformer(&p.source); transformer != nil { 205 return transformer.ArrayLen(&p.source) 206 } 207 if transformer := MapTransformer(&p.source); transformer != nil { 208 return transformer.Len(&p.source) 209 } 210 return 0 211 } 212 213 func (p *RawPayload) First() moleculer.Payload { 214 if transformer := ArrayTransformer(&p.source); transformer != nil && transformer.ArrayLen(&p.source) > 0 { 215 return New(transformer.First(&p.source)) 216 } 217 return New(nil) 218 } 219 220 //At returns the item at the given index 221 func (p *RawPayload) At(index int) moleculer.Payload { 222 if transformer := ArrayTransformer(&p.source); transformer != nil { 223 l := transformer.InterfaceArray(&p.source) 224 if index >= 0 && index < len(l) { 225 return New(l[index]) 226 } 227 } 228 return nil 229 } 230 231 func (p *RawPayload) Array() []moleculer.Payload { 232 if transformer := ArrayTransformer(&p.source); transformer != nil { 233 source := transformer.InterfaceArray(&p.source) 234 array := make([]moleculer.Payload, len(source)) 235 for index, item := range source { 236 array[index] = New(item) 237 } 238 return array 239 } 240 return nil 241 } 242 243 func (p *RawPayload) MapOver(transform func(in moleculer.Payload) moleculer.Payload) moleculer.Payload { 244 if p.IsArray() { 245 list := []moleculer.Payload{} 246 for _, value := range p.Array() { 247 list = append(list, transform(value)) 248 } 249 return New(list) 250 } else { 251 return Error("payload.MapOver can only deal with array payloads.") 252 } 253 } 254 255 func (p *RawPayload) ForEach(iterator func(key interface{}, value moleculer.Payload) bool) { 256 if p.IsArray() { 257 list := p.Array() 258 for index, value := range list { 259 if !iterator(index, value) { 260 break 261 } 262 } 263 } else if p.IsMap() { 264 mapValue := p.Map() 265 for key, value := range mapValue { 266 if !iterator(key, value) { 267 break 268 } 269 } 270 } else { 271 iterator(nil, p) 272 } 273 } 274 275 func (p *RawPayload) IsArray() bool { 276 transformer := ArrayTransformer(&p.source) 277 return transformer != nil 278 } 279 280 func (p *RawPayload) IsMap() bool { 281 transformer := MapTransformer(&p.source) 282 return transformer != nil 283 } 284 285 func (p *RawPayload) Float() float64 { 286 value, ok := p.source.(float64) 287 if !ok { 288 if transformer := getNumberTransformer(&p.source); transformer != nil { 289 value = transformer.toFloat64(&p.source) 290 } 291 } 292 return value 293 } 294 295 func (p *RawPayload) Float32() float32 { 296 value, ok := p.source.(float32) 297 if !ok { 298 if transformer := getNumberTransformer(&p.source); transformer != nil { 299 value = transformer.toFloat32(&p.source) 300 } 301 } 302 return value 303 } 304 305 func orderedKeys(m map[string]moleculer.Payload) []string { 306 keys := make([]string, len(m)) 307 i := 0 308 for key := range m { 309 keys[i] = key 310 i++ 311 } 312 sort.Strings(keys) 313 return keys 314 } 315 316 //mapToString takes in a map of payloads and return a string :) 317 func mapToString(m map[string]moleculer.Payload, ident string) string { 318 out := "(len=" + strconv.Itoa(len(m)) + ") {\n" 319 for _, key := range orderedKeys(m) { 320 out = out + ident + `"` + key + `": ` + m[key].String() + ",\n" 321 } 322 if len(m) == 0 { 323 out = out + "\n" 324 } 325 out = out + "}" 326 return out 327 } 328 329 //arrayToString takes in a list of payloads and return a string :) 330 func arrayToString(arr []moleculer.Payload, ident string) string { 331 out := "(array (len=" + strconv.Itoa(len(arr)) + ")) {\n" 332 lines := make([]string, len(arr)) 333 for index, item := range arr { 334 lines[index] = item.String() 335 } 336 sort.Strings(lines) 337 for _, item := range lines { 338 out = out + ident + item + ",\n" 339 } 340 if len(arr) == 0 { 341 out = out + "\n" 342 } 343 out = out + "}" 344 return out 345 } 346 347 type Stringer interface { 348 String() string 349 } 350 351 func (p *RawPayload) String() string { 352 s, isS := p.source.(string) 353 if isS { 354 return s 355 } 356 sr, isSr := p.source.(Stringer) 357 if isSr { 358 return sr.String() 359 } 360 return fmt.Sprint(p.source) 361 } 362 363 // func (p *RawPayload) StringIdented(ident string) string { 364 // if p.IsMap() { 365 // return mapToString(p.Map(), ident+" ") 366 // } 367 // if p.IsArray() { 368 // return arrayToString(p.Array(), ident+" ") 369 // } 370 // byteList, isBytes := p.source.([]byte) 371 // if isBytes { 372 // return string(byteList) 373 // } 374 // rawString, ok := p.source.(string) 375 // if ok { 376 // return rawString 377 // } 378 // return fmt.Sprintf("%v", p.source) 379 380 // } 381 382 func (p *RawPayload) Map() map[string]moleculer.Payload { 383 if transformer := MapTransformer(&p.source); transformer != nil { 384 source := transformer.AsMap(&p.source) 385 newMap := make(map[string]moleculer.Payload, len(source)) 386 for key, item := range source { 387 newPayload := RawPayload{item} 388 newMap[key] = &newPayload 389 } 390 return newMap 391 } 392 return nil 393 } 394 395 func (p *RawPayload) RawMap() map[string]interface{} { 396 if transformer := MapTransformer(&p.source); transformer != nil { 397 return transformer.AsMap(&p.source) 398 } 399 return nil 400 } 401 402 // TODO refactor out as a transformer.. just not depend on bson. 403 func (p *RawPayload) Bson() bson.M { 404 if GetValueType(&p.source) == "primitive.M" { 405 return p.source.(bson.M) 406 } 407 if p.IsMap() { 408 bm := bson.M{} 409 p.ForEach(func(key interface{}, value moleculer.Payload) bool { 410 skey := key.(string) 411 if value.IsArray() { 412 bm[skey] = value.BsonArray() 413 } else if value.IsMap() { 414 bm[skey] = value.Bson() 415 } else { 416 bm[skey] = value.Value() 417 } 418 return true 419 }) 420 return bm 421 } 422 return nil 423 } 424 425 func (p *RawPayload) BsonArray() bson.A { 426 if GetValueType(&p.source) == "[]primitive.A" { 427 return p.source.(bson.A) 428 } 429 if p.IsArray() { 430 ba := make(bson.A, p.Len()) 431 p.ForEach(func(index interface{}, value moleculer.Payload) bool { 432 if value.IsMap() { 433 ba[index.(int)] = value.Bson() 434 } else if value.IsArray() { 435 ba[index.(int)] = value.BsonArray() 436 } else { 437 ba[index.(int)] = value.Value() 438 } 439 return true 440 }) 441 return ba 442 } 443 return nil 444 } 445 446 // mapGet try to get the value at the path assuming the source is a map 447 func (p *RawPayload) mapGet(path string) (interface{}, bool) { 448 if transformer := MapTransformer(&p.source); transformer != nil { 449 return transformer.get(path, &p.source) 450 } 451 return nil, false 452 } 453 454 func isPath(s string) bool { 455 return strings.Contains(s, ".") 456 } 457 458 var indexedKey = regexp.MustCompile(`^(\w+)\[(\d+)\]$`) 459 460 //isIndexed checks if key is indexed e.g. stage[0] 461 func isIndexed(s string) bool { 462 return indexedKey.MatchString(s) 463 } 464 465 func splitIndex(s string) (key string, index int) { 466 parts := indexedKey.FindStringSubmatch(s) 467 key = parts[1] 468 index, _ = strconv.Atoi(parts[2]) 469 return key, index 470 } 471 472 func (p *RawPayload) Get(s string, defaultValue ...interface{}) moleculer.Payload { 473 if _, ok := p.mapGet(s); ok { 474 if defaultValue != nil { 475 return p.getKey(s, defaultValue...) 476 } 477 return p.getKey(s) 478 } 479 480 //check if is a path of key 481 if isPath(s) { 482 if defaultValue != nil { 483 return p.getPath(s, defaultValue...) 484 } 485 return p.getPath(s) 486 } 487 if isIndexed(s) { 488 k, index := splitIndex(s) 489 var v moleculer.Payload 490 if defaultValue != nil { 491 v = p.getKey(k, defaultValue...) 492 } else { 493 v = p.getKey(k) 494 } 495 return v.At(index) 496 } 497 if defaultValue != nil { 498 return p.getKey(s, defaultValue...) 499 } 500 return p.getKey(s) 501 } 502 503 //getPath get a value using a path expression e.g. address.country.code 504 // it also accepts indexed lists like address.options[0].label 505 func (p *RawPayload) getPath(path string, defaultValue ...interface{}) moleculer.Payload { 506 parts := strings.Split(path, ".") 507 k := parts[0] 508 v := p.Get(k, defaultValue...) 509 for i := 1; i < len(parts); i++ { 510 if v == nil { 511 return New(nil) 512 } 513 k = parts[i] 514 v = v.Get(k, defaultValue...) 515 } 516 return v 517 } 518 519 func (p *RawPayload) getKey(path string, defaultValue ...interface{}) moleculer.Payload { 520 if value, ok := p.mapGet(path); ok { 521 return New(value) 522 } 523 if len(defaultValue) > 1 { 524 return New(defaultValue) 525 } else if len(defaultValue) > 0 { 526 return New(defaultValue[0]) 527 } 528 return New(nil) 529 } 530 531 //Only return a payload containing only the field specified 532 func (p *RawPayload) Only(path string) moleculer.Payload { 533 if value, ok := p.mapGet(path); ok { 534 return New(map[string]interface{}{path: value}) 535 } 536 return New(nil) 537 } 538 539 func (p *RawPayload) Value() interface{} { 540 return p.source 541 } 542 543 func match(key string, options []string) bool { 544 for _, item := range options { 545 if item == key { 546 return true 547 } 548 } 549 return false 550 } 551 552 type Sortable struct { 553 Field string 554 List []moleculer.Payload 555 } 556 557 func (s *Sortable) Len() int { 558 return len(s.List) 559 } 560 561 // Less reports whether the element with 562 // index i should sort before the element with index j. 563 func (s *Sortable) Less(i, j int) bool { 564 vi := s.List[i].Get(s.Field) 565 vj := s.List[j].Get(s.Field) 566 return vi.String() < vj.String() 567 } 568 569 // Swap swaps the elements with indexes i and j. 570 func (s *Sortable) Swap(i, j int) { 571 vi := s.List[i] 572 vj := s.List[j] 573 s.List[j] = vi 574 s.List[i] = vj 575 } 576 577 func (s *Sortable) Payload() moleculer.Payload { 578 return New(s.List) 579 } 580 581 func (p *RawPayload) Sort(field string) moleculer.Payload { 582 if !p.IsArray() { 583 return p 584 } 585 ps := &Sortable{field, p.Array()} 586 sort.Sort(ps) 587 return ps.Payload() 588 } 589 590 func (p *RawPayload) Remove(fields ...string) moleculer.Payload { 591 if p.IsMap() { 592 new := map[string]interface{}{} 593 for key, value := range p.RawMap() { 594 if !match(key, fields) { 595 new[key] = value 596 } 597 } 598 return New(new) 599 } 600 if p.IsArray() { 601 arr := p.Array() 602 new := make([]moleculer.Payload, len(arr)) 603 for index, item := range arr { 604 new[index] = item.Remove(fields...) 605 } 606 return New(new) 607 } 608 return Error("payload.Remove can only deal with map and array payloads.") 609 } 610 611 func (p *RawPayload) AddItem(value interface{}) moleculer.Payload { 612 if !p.IsArray() { 613 return Error("payload.AddItem can only deal with lists/arrays.") 614 } 615 arr := p.Array() 616 arr = append(arr, New(value)) 617 return New(arr) 618 } 619 620 //Add add the field:value pair to the existing values and return a new payload. 621 func (p *RawPayload) Add(field string, value interface{}) moleculer.Payload { 622 if !p.IsMap() { 623 return Error("payload.Add can only deal with map payloads.") 624 } 625 m := p.RawMap() 626 m[field] = value 627 return New(m) 628 } 629 630 //AddMany merge the maps with eh existing values and return a new payload. 631 func (p *RawPayload) AddMany(toAdd map[string]interface{}) moleculer.Payload { 632 if !p.IsMap() { 633 return Error("payload.Add can only deal with map payloads.") 634 } 635 m := p.RawMap() 636 for key, value := range toAdd { 637 m[key] = value 638 } 639 return New(m) 640 } 641 642 func Error(msgs ...interface{}) moleculer.Payload { 643 return New(errors.New(fmt.Sprint(msgs...))) 644 } 645 646 type payloadError struct { 647 err string 648 payload moleculer.Payload 649 } 650 651 func (e payloadError) Error() string { 652 return e.err 653 } 654 655 func PayloadError(msg string, p moleculer.Payload) moleculer.Payload { 656 return &RawPayload{source: payloadError{msg, p}} 657 } 658 659 func (p *RawPayload) ErrorPayload() moleculer.Payload { 660 pError, ok := p.source.(payloadError) 661 if ok { 662 return pError.payload 663 } 664 return nil 665 } 666 667 func EmptyList() moleculer.Payload { 668 return &RawPayload{source: []interface{}{}} 669 } 670 671 func Empty() moleculer.Payload { 672 return &RawPayload{source: map[string]interface{}{}} 673 } 674 675 func New(source interface{}) moleculer.Payload { 676 pl, isPayload := source.(moleculer.Payload) 677 if isPayload { 678 return pl 679 } 680 return &RawPayload{source} 681 }