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 }