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  }