github.com/futurehomeno/fimpgo@v1.14.0/message.go (about)

     1  package fimpgo
     2  
     3  import (
     4  	"encoding/base64"
     5  	"encoding/json"
     6  	"fmt"
     7  	"reflect"
     8  	"strconv"
     9  	"time"
    10  
    11  	"github.com/buger/jsonparser"
    12  	"github.com/google/uuid"
    13  )
    14  
    15  const (
    16  	TimeFormat       = "2006-01-02T15:04:05.999Z07:00"
    17  	VTypeString      = "string"
    18  	VTypeInt         = "int"
    19  	VTypeFloat       = "float"
    20  	VTypeBool        = "bool"
    21  	VTypeStrMap      = "str_map"
    22  	VTypeIntMap      = "int_map"
    23  	VTypeFloatMap    = "float_map"
    24  	VTypeBoolMap     = "bool_map"
    25  	VTypeStrArray    = "str_array"
    26  	VTypeIntArray    = "int_array"
    27  	VTypeFloatArray  = "float_array"
    28  	VTypeBoolArray   = "bool_array"
    29  	VTypeObject      = "object"
    30  	VTypeBase64      = "base64"
    31  	VTypeBinary      = "bin"
    32  	VTypeNull        = "null"
    33  	wrongValueFormat = "wrong value type. expected %+v, got %+v"
    34  
    35  	Val = "val"
    36  )
    37  
    38  var timestampFormats = []string{
    39  	time.RFC3339Nano,
    40  	"2006-01-02T15:04:05.999999999Z0700",
    41  	"2006-01-02 15:04:05.999999999 Z0700",
    42  	"2006-01-02 15:04:05.999999999 Z07:00",
    43  }
    44  
    45  type Props map[string]string
    46  
    47  func (p Props) GetIntValue(key string) (int64, bool, error) {
    48  	val, ok := p[key]
    49  	if !ok {
    50  		return 0, false, nil
    51  	}
    52  
    53  	i, err := strconv.ParseInt(val, 10, 64)
    54  	if err != nil {
    55  		return 0, true, fmt.Errorf("property %s has wrong value type, expected int, got %s", key, val)
    56  	}
    57  
    58  	return i, true, nil
    59  }
    60  
    61  func (p Props) GetStringValue(key string) (string, bool) {
    62  	val, ok := p[key]
    63  	if !ok {
    64  		return "", false
    65  	}
    66  
    67  	return val, true
    68  }
    69  
    70  func (p Props) GetFloatValue(key string) (float64, bool, error) {
    71  	val, ok := p[key]
    72  	if !ok {
    73  		return 0, false, nil
    74  	}
    75  
    76  	f, err := strconv.ParseFloat(val, 64)
    77  	if err != nil {
    78  		return 0, true, fmt.Errorf("property %s has wrong value type, expected float, got %s", key, val)
    79  	}
    80  
    81  	return f, true, nil
    82  }
    83  
    84  func (p Props) GetBoolValue(key string) (bool, bool, error) {
    85  	val, ok := p[key]
    86  	if !ok {
    87  		return false, false, nil
    88  	}
    89  
    90  	b, err := strconv.ParseBool(val)
    91  	if err != nil {
    92  		return false, true, fmt.Errorf("property %s has wrong value type, expected bool, got %s", key, val)
    93  	}
    94  
    95  	return b, true, nil
    96  }
    97  
    98  func (p Props) GetTimestampValue(key string) (time.Time, bool, error) {
    99  	val, ok := p[key]
   100  	if !ok {
   101  		return time.Time{}, false, nil
   102  	}
   103  
   104  	t := ParseTime(val)
   105  	if t.IsZero() {
   106  		return time.Time{}, true, fmt.Errorf("property %s has wrong value type, expected RFC3339 timestamp, got %s", key, val)
   107  	}
   108  
   109  	return t, true, nil
   110  }
   111  
   112  type Tags []string
   113  
   114  // Storage is used to define optional message storage strategy.
   115  type Storage struct {
   116  	Strategy StorageStrategy `json:"strategy,omitempty"`
   117  	SubValue string          `json:"sub_value,omitempty"`
   118  }
   119  
   120  // StorageStrategy defines message storage strategy.
   121  type StorageStrategy string
   122  
   123  // Constants defining storage strategies.
   124  const (
   125  	StorageStrategyAggregate StorageStrategy = "aggregate"
   126  	StorageStrategySkip      StorageStrategy = "skip"
   127  	StorageStrategySplit     StorageStrategy = "split"
   128  )
   129  
   130  type FimpMessage struct {
   131  	Type            string      `json:"type"`
   132  	Service         string      `json:"serv"`
   133  	ValueType       string      `json:"val_t"`
   134  	Value           interface{} `json:"val"`
   135  	ValueObj        []byte      `json:"-"`
   136  	Tags            Tags        `json:"tags"`
   137  	Properties      Props       `json:"props"`
   138  	Storage         *Storage    `json:"storage,omitempty"`
   139  	Version         string      `json:"ver"`
   140  	CorrelationID   string      `json:"corid"`
   141  	ResponseToTopic string      `json:"resp_to,omitempty"`
   142  	Source          string      `json:"src,omitempty"`
   143  	CreationTime    string      `json:"ctime"`
   144  	UID             string      `json:"uid"`
   145  	Topic           string      `json:"topic,omitempty"` // The field should be used to store original topic. It can be useful for converting message from MQTT to other transports.
   146  }
   147  
   148  func (msg *FimpMessage) SetValue(value interface{}, valType string) {
   149  	msg.Value = value
   150  	msg.ValueType = valType
   151  }
   152  
   153  func (msg *FimpMessage) GetIntValue() (int64, error) {
   154  	val, ok := msg.Value.(int64)
   155  	if ok {
   156  		return val, nil
   157  	}
   158  	return 0, fmt.Errorf(wrongValueFormat, "int64", reflect.ValueOf(msg.Value))
   159  }
   160  
   161  func (msg *FimpMessage) GetStringValue() (string, error) {
   162  	val, ok := msg.Value.(string)
   163  	if ok {
   164  		return val, nil
   165  	}
   166  	return "", fmt.Errorf(wrongValueFormat, "string", reflect.ValueOf(msg.Value))
   167  }
   168  
   169  func (msg *FimpMessage) GetBoolValue() (bool, error) {
   170  	val, ok := msg.Value.(bool)
   171  	if ok {
   172  		return val, nil
   173  	}
   174  	return false, fmt.Errorf(wrongValueFormat, "bool", reflect.ValueOf(msg.Value))
   175  }
   176  
   177  func (msg *FimpMessage) GetFloatValue() (float64, error) {
   178  	val, ok := msg.Value.(float64)
   179  	if ok {
   180  		return val, nil
   181  	}
   182  	return 0, fmt.Errorf(wrongValueFormat, "float64", reflect.ValueOf(msg.Value))
   183  }
   184  
   185  func (msg *FimpMessage) GetStrArrayValue() ([]string, error) {
   186  	val, ok := msg.Value.([]string)
   187  	if ok {
   188  		return val, nil
   189  	}
   190  	return nil, fmt.Errorf(wrongValueFormat, "[]string", reflect.ValueOf(msg.Value))
   191  }
   192  
   193  func (msg *FimpMessage) GetIntArrayValue() ([]int64, error) {
   194  	val, ok := msg.Value.([]int64)
   195  	if ok {
   196  		return val, nil
   197  	}
   198  	return nil, fmt.Errorf(wrongValueFormat, "[]int64]", reflect.ValueOf(msg.Value))
   199  }
   200  
   201  func (msg *FimpMessage) GetFloatArrayValue() ([]float64, error) {
   202  	val, ok := msg.Value.([]float64)
   203  	if ok {
   204  		return val, nil
   205  	}
   206  	return nil, fmt.Errorf(wrongValueFormat, "[]float64", reflect.ValueOf(msg.Value))
   207  }
   208  
   209  func (msg *FimpMessage) GetBoolArrayValue() ([]bool, error) {
   210  	val, ok := msg.Value.([]bool)
   211  	if ok {
   212  		return val, nil
   213  	}
   214  	return nil, fmt.Errorf(wrongValueFormat, "[]bool", reflect.ValueOf(msg.Value))
   215  }
   216  
   217  func (msg *FimpMessage) GetStrMapValue() (map[string]string, error) {
   218  	val, ok := msg.Value.(map[string]string)
   219  	if ok {
   220  		return val, nil
   221  	}
   222  	return nil, fmt.Errorf(wrongValueFormat, "map[string]string", reflect.ValueOf(msg.Value))
   223  }
   224  
   225  func (msg *FimpMessage) GetIntMapValue() (map[string]int64, error) {
   226  	val, ok := msg.Value.(map[string]int64)
   227  	if ok {
   228  		return val, nil
   229  	}
   230  	return nil, fmt.Errorf(wrongValueFormat, "map[string]int64", reflect.ValueOf(msg.Value))
   231  }
   232  
   233  func (msg *FimpMessage) GetFloatMapValue() (map[string]float64, error) {
   234  	val, ok := msg.Value.(map[string]float64)
   235  	if ok {
   236  		return val, nil
   237  	}
   238  	return nil, fmt.Errorf(wrongValueFormat, "map[string]float64", reflect.ValueOf(msg.Value))
   239  }
   240  
   241  func (msg *FimpMessage) GetBoolMapValue() (map[string]bool, error) {
   242  	val, ok := msg.Value.(map[string]bool)
   243  	if ok {
   244  		return val, nil
   245  	}
   246  	return nil, fmt.Errorf(wrongValueFormat, "map[string]bool", reflect.ValueOf(msg.Value))
   247  }
   248  
   249  func (msg *FimpMessage) GetRawObjectValue() []byte {
   250  	return msg.ValueObj
   251  }
   252  
   253  func (msg *FimpMessage) GetObjectValue(objectBindVar interface{}) error {
   254  	return json.Unmarshal(msg.ValueObj, objectBindVar)
   255  }
   256  
   257  func (msg *FimpMessage) SerializeToJson() ([]byte, error) {
   258  	jsonBA, err := json.Marshal(msg)
   259  	if msg.ValueType == VTypeObject {
   260  		if msg.Value == nil && msg.ValueObj != nil {
   261  			// This is for object pass though.
   262  			jsonBA, err = jsonparser.Set(jsonBA, msg.ValueObj, "val")
   263  		}
   264  	}
   265  	return jsonBA, err
   266  
   267  }
   268  
   269  // GetCreationTime returns parsed creation time of the message.
   270  func (msg *FimpMessage) GetCreationTime() time.Time {
   271  	return ParseTime(msg.CreationTime)
   272  }
   273  
   274  // WithStorageStrategy sets storage strategy for the message.
   275  func (msg *FimpMessage) WithStorageStrategy(strategy StorageStrategy, subValue string) *FimpMessage {
   276  	msg.Storage = &Storage{Strategy: strategy, SubValue: subValue}
   277  
   278  	return msg
   279  }
   280  
   281  // WithProperty sets property for the message.
   282  func (msg *FimpMessage) WithProperty(property, value string) *FimpMessage {
   283  	if msg.Properties == nil {
   284  		msg.Properties = make(Props)
   285  	}
   286  
   287  	msg.Properties[property] = value
   288  
   289  	return msg
   290  }
   291  
   292  // WithTag adds tag to the message.
   293  func (msg *FimpMessage) WithTag(tag string) *FimpMessage {
   294  	msg.Tags = append(msg.Tags, tag)
   295  
   296  	return msg
   297  }
   298  
   299  func NewMessage(type_ string, service string, valueType string, value interface{}, props Props, tags Tags, requestMessage *FimpMessage) *FimpMessage {
   300  	msg := FimpMessage{Type: type_,
   301  		Service:      service,
   302  		ValueType:    valueType,
   303  		Value:        value,
   304  		Tags:         tags,
   305  		Properties:   props,
   306  		Version:      "1",
   307  		CreationTime: time.Now().Format(TimeFormat),
   308  		UID:          uuid.New().String(),
   309  	}
   310  
   311  	if requestMessage != nil {
   312  		msg.CorrelationID = requestMessage.UID
   313  	}
   314  
   315  	return &msg
   316  }
   317  
   318  func NewNullMessage(type_ string, service string, props Props, tags Tags, requestMessage *FimpMessage) *FimpMessage {
   319  	return NewMessage(type_, service, VTypeNull, nil, props, tags, requestMessage)
   320  }
   321  
   322  func NewStringMessage(type_ string, service string, value string, props Props, tags Tags, requestMessage *FimpMessage) *FimpMessage {
   323  	return NewMessage(type_, service, VTypeString, value, props, tags, requestMessage)
   324  }
   325  
   326  func NewIntMessage(type_ string, service string, value int64, props Props, tags Tags, requestMessage *FimpMessage) *FimpMessage {
   327  	return NewMessage(type_, service, VTypeInt, value, props, tags, requestMessage)
   328  }
   329  
   330  func NewFloatMessage(type_ string, service string, value float64, props Props, tags Tags, requestMessage *FimpMessage) *FimpMessage {
   331  	return NewMessage(type_, service, VTypeFloat, value, props, tags, requestMessage)
   332  }
   333  
   334  func NewBoolMessage(type_ string, service string, value bool, props Props, tags Tags, requestMessage *FimpMessage) *FimpMessage {
   335  	return NewMessage(type_, service, VTypeBool, value, props, tags, requestMessage)
   336  }
   337  
   338  func NewStrArrayMessage(type_ string, service string, value []string, props Props, tags Tags, requestMessage *FimpMessage) *FimpMessage {
   339  	return NewMessage(type_, service, VTypeStrArray, value, props, tags, requestMessage)
   340  }
   341  
   342  func NewIntArrayMessage(type_ string, service string, value []int64, props Props, tags Tags, requestMessage *FimpMessage) *FimpMessage {
   343  	return NewMessage(type_, service, VTypeIntArray, value, props, tags, requestMessage)
   344  }
   345  
   346  func NewFloatArrayMessage(type_ string, service string, value []float64, props Props, tags Tags, requestMessage *FimpMessage) *FimpMessage {
   347  	return NewMessage(type_, service, VTypeFloatArray, value, props, tags, requestMessage)
   348  }
   349  
   350  func NewBoolArrayMessage(type_ string, service string, value []bool, props Props, tags Tags, requestMessage *FimpMessage) *FimpMessage {
   351  	return NewMessage(type_, service, VTypeBoolArray, value, props, tags, requestMessage)
   352  }
   353  
   354  func NewStrMapMessage(type_ string, service string, value map[string]string, props Props, tags Tags, requestMessage *FimpMessage) *FimpMessage {
   355  	return NewMessage(type_, service, VTypeStrMap, value, props, tags, requestMessage)
   356  }
   357  
   358  func NewIntMapMessage(type_ string, service string, value map[string]int64, props Props, tags Tags, requestMessage *FimpMessage) *FimpMessage {
   359  	return NewMessage(type_, service, VTypeIntMap, value, props, tags, requestMessage)
   360  }
   361  
   362  func NewFloatMapMessage(type_ string, service string, value map[string]float64, props Props, tags Tags, requestMessage *FimpMessage) *FimpMessage {
   363  	return NewMessage(type_, service, VTypeFloatMap, value, props, tags, requestMessage)
   364  }
   365  
   366  func NewBoolMapMessage(type_ string, service string, value map[string]bool, props Props, tags Tags, requestMessage *FimpMessage) *FimpMessage {
   367  	return NewMessage(type_, service, VTypeBoolMap, value, props, tags, requestMessage)
   368  }
   369  
   370  func NewObjectMessage(type_ string, service string, value interface{}, props Props, tags Tags, requestMessage *FimpMessage) *FimpMessage {
   371  	return NewMessage(type_, service, VTypeObject, value, props, tags, requestMessage)
   372  }
   373  
   374  // NewBinaryMessage transport message is meant to carry original message using either encryption , signing or
   375  func NewBinaryMessage(type_, service string, value []byte, props Props, tags Tags, requestMessage *FimpMessage) *FimpMessage {
   376  	valEnc := base64.StdEncoding.EncodeToString(value)
   377  	return NewMessage(type_, service, VTypeBinary, valEnc, props, tags, requestMessage)
   378  }
   379  
   380  func NewMessageFromBytes(msg []byte) (*FimpMessage, error) {
   381  	fimpmsg := FimpMessage{}
   382  	var err error
   383  	fimpmsg.Type, err = jsonparser.GetString(msg, "type")
   384  	fimpmsg.Service, err = jsonparser.GetString(msg, "serv")
   385  	fimpmsg.ValueType, err = jsonparser.GetString(msg, "val_t")
   386  	fimpmsg.UID, _ = jsonparser.GetString(msg, "uid")
   387  	fimpmsg.CorrelationID, _ = jsonparser.GetString(msg, "corid")
   388  	fimpmsg.CreationTime, _ = jsonparser.GetString(msg, "ctime")
   389  	fimpmsg.ResponseToTopic, _ = jsonparser.GetString(msg, "resp_to")
   390  	fimpmsg.Source, _ = jsonparser.GetString(msg, "src")
   391  	fimpmsg.Topic, _ = jsonparser.GetString(msg, "topic")
   392  	fimpmsg.Version, _ = jsonparser.GetString(msg, "ver")
   393  
   394  	switch fimpmsg.ValueType {
   395  	case VTypeString:
   396  		fimpmsg.Value, err = jsonparser.GetString(msg, "val")
   397  	case VTypeBool:
   398  		fimpmsg.Value, err = jsonparser.GetBoolean(msg, "val")
   399  	case VTypeInt:
   400  		fimpmsg.Value, err = jsonparser.GetInt(msg, "val")
   401  	case VTypeFloat:
   402  		fimpmsg.Value, err = jsonparser.GetFloat(msg, "val")
   403  	case VTypeBoolArray:
   404  		val := make([]bool, 0)
   405  		if _, err := jsonparser.ArrayEach(msg, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
   406  			item, _ := jsonparser.ParseBoolean(value)
   407  			val = append(val, item)
   408  		}, "val"); err != nil {
   409  			return nil, err
   410  		}
   411  
   412  		fimpmsg.Value = val
   413  	case VTypeStrArray:
   414  		val := make([]string, 0)
   415  		if _, err := jsonparser.ArrayEach(msg, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
   416  			item, _ := jsonparser.ParseString(value)
   417  			val = append(val, item)
   418  		}, "val"); err != nil {
   419  			return nil, err
   420  		}
   421  
   422  		fimpmsg.Value = val
   423  	case VTypeIntArray:
   424  		val := make([]int64, 0)
   425  		if _, err := jsonparser.ArrayEach(msg, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
   426  			item, _ := jsonparser.ParseInt(value)
   427  			val = append(val, item)
   428  
   429  		}, "val"); err != nil {
   430  			return nil, err
   431  		}
   432  		fimpmsg.Value = val
   433  	case VTypeFloatArray:
   434  		val := make([]float64, 0)
   435  		if _, err := jsonparser.ArrayEach(msg, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
   436  			item, _ := jsonparser.ParseFloat(value)
   437  			val = append(val, item)
   438  		}, "val"); err != nil {
   439  			return nil, err
   440  		}
   441  		fimpmsg.Value = val
   442  
   443  	case VTypeStrMap:
   444  		val := make(map[string]string)
   445  		if err := jsonparser.ObjectEach(msg, func(key []byte, value []byte, dataType jsonparser.ValueType, offset int) error {
   446  			val[string(key)], err = jsonparser.ParseString(value)
   447  			return nil
   448  		}, "val"); err != nil {
   449  			return nil, err
   450  		}
   451  		fimpmsg.Value = val
   452  
   453  	case VTypeIntMap:
   454  		val := make(map[string]int64)
   455  		if err := jsonparser.ObjectEach(msg, func(key []byte, value []byte, dataType jsonparser.ValueType, offset int) error {
   456  			val[string(key)], err = jsonparser.ParseInt(value)
   457  			return nil
   458  		}, "val"); err != nil {
   459  			return nil, err
   460  		}
   461  		fimpmsg.Value = val
   462  
   463  	case VTypeFloatMap:
   464  		val := make(map[string]float64)
   465  		if err := jsonparser.ObjectEach(msg, func(key []byte, value []byte, dataType jsonparser.ValueType, offset int) error {
   466  			val[string(key)], err = jsonparser.ParseFloat(value)
   467  			return nil
   468  		}, "val"); err != nil {
   469  			return nil, err
   470  		}
   471  		fimpmsg.Value = val
   472  
   473  	case VTypeBoolMap:
   474  		val := make(map[string]bool)
   475  		if err := jsonparser.ObjectEach(msg, func(key []byte, value []byte, dataType jsonparser.ValueType, offset int) error {
   476  			val[string(key)], err = jsonparser.ParseBoolean(value)
   477  			return nil
   478  		}, "val"); err != nil {
   479  			return nil, err
   480  		}
   481  		fimpmsg.Value = val
   482  
   483  	case VTypeBinary:
   484  		fimpmsg.Value, err = jsonparser.GetString(msg, "val")
   485  		//base64val, err := jsonparser.GetString(msg, "val")
   486  		//if err != nil {
   487  		//	return nil,err
   488  		//}
   489  		//fimpmsg.Value ,err = base64.StdEncoding.DecodeString(base64val)
   490  		//if err != nil {
   491  		//	return nil,err
   492  		//}
   493  
   494  	case VTypeObject:
   495  		fimpmsg.ValueObj, _, _, err = jsonparser.Get(msg, "val")
   496  
   497  	}
   498  
   499  	if properties, dt, _, err := jsonparser.Get(msg, "props"); dt != jsonparser.NotExist && dt != jsonparser.Null && err == nil {
   500  		err := json.Unmarshal(properties, &fimpmsg.Properties)
   501  		if err != nil {
   502  			return nil, err
   503  		}
   504  	}
   505  
   506  	if storage, dt, _, err := jsonparser.Get(msg, "storage"); dt != jsonparser.NotExist && dt != jsonparser.Null && err == nil {
   507  		err := json.Unmarshal(storage, &fimpmsg.Storage)
   508  		if err != nil {
   509  			return nil, err
   510  		}
   511  	}
   512  
   513  	if tags, dt, _, err := jsonparser.Get(msg, "tags"); dt != jsonparser.NotExist && dt != jsonparser.Null && err == nil {
   514  		err := json.Unmarshal(tags, &fimpmsg.Tags)
   515  		if err != nil {
   516  			return nil, err
   517  		}
   518  	}
   519  
   520  	return &fimpmsg, err
   521  }
   522  
   523  // ParseTime is a helper function to parse a timestamp from a string from various variations of RFC3339.
   524  func ParseTime(timestamp string) time.Time {
   525  	for _, format := range timestampFormats {
   526  		t, err := time.Parse(format, timestamp)
   527  		if err == nil {
   528  			return t
   529  		}
   530  	}
   531  
   532  	return time.Time{}
   533  }