315 lines
10 KiB
Go
315 lines
10 KiB
Go
package thinkingdata
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
Track = "track"
|
|
TrackUpdate = "track_update"
|
|
TrackOverwrite = "track_overwrite"
|
|
UserSet = "user_set"
|
|
UserUnset = "user_unset"
|
|
UserSetOnce = "user_setOnce"
|
|
UserAdd = "user_add"
|
|
UserAppend = "user_append"
|
|
UserUniqAppend = "user_uniq_append"
|
|
UserDel = "user_del"
|
|
|
|
SdkVersion = "2.0.3"
|
|
LibName = "Golang"
|
|
)
|
|
|
|
type Data struct {
|
|
IsComplex bool `json:"-"` // properties are nested or not
|
|
AccountId string `json:"#account_id,omitempty"`
|
|
DistinctId string `json:"#distinct_id,omitempty"`
|
|
Type string `json:"#type"`
|
|
Time string `json:"#time"`
|
|
Timestamp int64 `json:"#timestamp,omitempty"`
|
|
EventName string `json:"#event_name,omitempty"`
|
|
EventId string `json:"#event_id,omitempty"`
|
|
FirstCheckId string `json:"#first_check_id,omitempty"`
|
|
Ip string `json:"#ip,omitempty"`
|
|
UUID string `json:"#uuid,omitempty"`
|
|
AppId string `json:"#app_id,omitempty"`
|
|
Properties map[string]interface{} `json:"properties"`
|
|
PropertiesSummary string `json:"properties_summary,omitempty"`
|
|
}
|
|
|
|
// TDConsumer define operation interface
|
|
type TDConsumer interface {
|
|
Add(d Data) error
|
|
Flush() error
|
|
Close() error
|
|
IsStringent() bool // check data or not.
|
|
}
|
|
|
|
type TDAnalytics struct {
|
|
consumer TDConsumer
|
|
superProperties map[string]interface{}
|
|
mutex *sync.RWMutex
|
|
dynamicSuperProperties func() map[string]interface{}
|
|
}
|
|
|
|
// New init SDK
|
|
func New(c TDConsumer) TDAnalytics {
|
|
tdLogInfo("init SDK success")
|
|
return TDAnalytics{
|
|
consumer: c,
|
|
superProperties: make(map[string]interface{}),
|
|
mutex: new(sync.RWMutex),
|
|
}
|
|
}
|
|
|
|
// GetSuperProperties get common properties
|
|
func (ta *TDAnalytics) GetSuperProperties() map[string]interface{} {
|
|
result := make(map[string]interface{})
|
|
ta.mutex.Lock()
|
|
mergeProperties(result, ta.superProperties)
|
|
ta.mutex.Unlock()
|
|
return result
|
|
}
|
|
|
|
// SetSuperProperties set common properties
|
|
func (ta *TDAnalytics) SetSuperProperties(superProperties map[string]interface{}) {
|
|
ta.mutex.Lock()
|
|
mergeProperties(ta.superProperties, superProperties)
|
|
ta.mutex.Unlock()
|
|
}
|
|
|
|
// ClearSuperProperties clear common properties
|
|
func (ta *TDAnalytics) ClearSuperProperties() {
|
|
ta.mutex.Lock()
|
|
ta.superProperties = make(map[string]interface{})
|
|
ta.mutex.Unlock()
|
|
}
|
|
|
|
// SetDynamicSuperProperties set common properties dynamically.
|
|
// not recommend to add the operation which with a lot of computation
|
|
func (ta *TDAnalytics) SetDynamicSuperProperties(action func() map[string]interface{}) {
|
|
ta.mutex.Lock()
|
|
ta.dynamicSuperProperties = action
|
|
ta.mutex.Unlock()
|
|
}
|
|
|
|
// GetDynamicSuperProperties dynamic common properties
|
|
func (ta *TDAnalytics) GetDynamicSuperProperties() map[string]interface{} {
|
|
result := make(map[string]interface{})
|
|
ta.mutex.RLock()
|
|
if ta.dynamicSuperProperties != nil {
|
|
mergeProperties(result, ta.dynamicSuperProperties())
|
|
}
|
|
ta.mutex.RUnlock()
|
|
return result
|
|
}
|
|
|
|
// Track report ordinary event
|
|
func (ta *TDAnalytics) Track(accountId, distinctId, eventName string, properties map[string]interface{}) error {
|
|
return ta.track(accountId, distinctId, Track, eventName, "", properties)
|
|
}
|
|
|
|
// TrackFirst report first event
|
|
func (ta *TDAnalytics) TrackFirst(accountId, distinctId, eventName, firstCheckId string, properties map[string]interface{}) error {
|
|
if len(firstCheckId) == 0 {
|
|
msg := "the 'firstCheckId' must be provided"
|
|
tdLogInfo(msg)
|
|
return errors.New(msg)
|
|
}
|
|
p := make(map[string]interface{})
|
|
mergeProperties(p, properties)
|
|
p["#first_check_id"] = firstCheckId
|
|
return ta.track(accountId, distinctId, Track, eventName, "", p)
|
|
}
|
|
|
|
// TrackUpdate report updatable event
|
|
func (ta *TDAnalytics) TrackUpdate(accountId, distinctId, eventName, eventId string, properties map[string]interface{}) error {
|
|
return ta.track(accountId, distinctId, TrackUpdate, eventName, eventId, properties)
|
|
}
|
|
|
|
// TrackOverwrite report overridable event
|
|
func (ta *TDAnalytics) TrackOverwrite(accountId, distinctId, eventName, eventId string, properties map[string]interface{}) error {
|
|
return ta.track(accountId, distinctId, TrackOverwrite, eventName, eventId, properties)
|
|
}
|
|
|
|
func (ta *TDAnalytics) track(accountId, distinctId, dataType, eventName, eventId string, properties map[string]interface{}) error {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
tdLogError("%+v\ndata: %+v", r, properties)
|
|
}
|
|
}()
|
|
|
|
if len(eventName) == 0 {
|
|
msg := "the event name must be provided"
|
|
tdLogError(msg)
|
|
return errors.New(msg)
|
|
}
|
|
|
|
// eventId not be null unless eventType is equal Track.
|
|
if len(eventId) == 0 && dataType != Track {
|
|
msg := "the event id must be provided"
|
|
tdLogError(msg)
|
|
return errors.New(msg)
|
|
}
|
|
|
|
p := ta.GetSuperProperties()
|
|
dynamicSuperProperties := ta.GetDynamicSuperProperties()
|
|
|
|
mergeProperties(p, dynamicSuperProperties)
|
|
// preset properties has the highest priority
|
|
p["#lib"] = LibName
|
|
p["#lib_version"] = SdkVersion
|
|
// custom properties
|
|
mergeProperties(p, properties)
|
|
|
|
return ta.add(accountId, distinctId, dataType, eventName, eventId, p)
|
|
}
|
|
|
|
// UserSet set user properties. would overwrite existing names.
|
|
func (ta *TDAnalytics) UserSet(accountId string, distinctId string, properties map[string]interface{}) error {
|
|
return ta.user(accountId, distinctId, UserSet, properties)
|
|
}
|
|
|
|
// UserUnset clear the user properties of users.
|
|
func (ta *TDAnalytics) UserUnset(accountId string, distinctId string, s []string) error {
|
|
if len(s) == 0 {
|
|
msg := "invalid params for UserUnset: keys is nil"
|
|
tdLogInfo(msg)
|
|
return errors.New(msg)
|
|
}
|
|
prop := make(map[string]interface{})
|
|
for _, v := range s {
|
|
prop[v] = 0
|
|
}
|
|
return ta.user(accountId, distinctId, UserUnset, prop)
|
|
}
|
|
|
|
func (ta *TDAnalytics) UserUnsetWithProperties(accountId string, distinctId string, properties map[string]interface{}) error {
|
|
if len(properties) == 0 {
|
|
msg := "invalid params for UserUnset: properties is nil"
|
|
tdLogInfo(msg)
|
|
return errors.New(msg)
|
|
}
|
|
return ta.user(accountId, distinctId, UserUnset, properties)
|
|
}
|
|
|
|
// UserSetOnce set user properties, If such property had been set before, this message would be neglected.
|
|
func (ta *TDAnalytics) UserSetOnce(accountId string, distinctId string, properties map[string]interface{}) error {
|
|
return ta.user(accountId, distinctId, UserSetOnce, properties)
|
|
}
|
|
|
|
// UserAdd to accumulate operations against the property.
|
|
func (ta *TDAnalytics) UserAdd(accountId string, distinctId string, properties map[string]interface{}) error {
|
|
return ta.user(accountId, distinctId, UserAdd, properties)
|
|
}
|
|
|
|
// UserAppend to add user properties of array type.
|
|
func (ta *TDAnalytics) UserAppend(accountId string, distinctId string, properties map[string]interface{}) error {
|
|
return ta.user(accountId, distinctId, UserAppend, properties)
|
|
}
|
|
|
|
// UserUniqAppend append user properties to array type by unique.
|
|
func (ta *TDAnalytics) UserUniqAppend(accountId string, distinctId string, properties map[string]interface{}) error {
|
|
return ta.user(accountId, distinctId, UserUniqAppend, properties)
|
|
}
|
|
|
|
// UserDelete delete a user, This operation cannot be undone.
|
|
func (ta *TDAnalytics) UserDelete(accountId string, distinctId string) error {
|
|
return ta.user(accountId, distinctId, UserDel, nil)
|
|
}
|
|
|
|
// UserDeleteWithProperties delete a user, This operation cannot be undone.
|
|
func (ta *TDAnalytics) UserDeleteWithProperties(accountId string, distinctId string, properties map[string]interface{}) error {
|
|
return ta.user(accountId, distinctId, UserDel, properties)
|
|
}
|
|
|
|
func (ta *TDAnalytics) user(accountId, distinctId, dataType string, properties map[string]interface{}) error {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
tdLogError("%+v\ndata: %+v", r, properties)
|
|
}
|
|
}()
|
|
if properties == nil && dataType != UserDel {
|
|
msg := "invalid params for " + dataType + ": properties is nil"
|
|
tdLogError(msg)
|
|
return errors.New(msg)
|
|
}
|
|
p := make(map[string]interface{})
|
|
mergeProperties(p, properties)
|
|
return ta.add(accountId, distinctId, dataType, "", "", p)
|
|
}
|
|
|
|
// Flush report data immediately.
|
|
func (ta *TDAnalytics) Flush() error {
|
|
return ta.consumer.Flush()
|
|
}
|
|
|
|
// Close and exit sdk
|
|
func (ta *TDAnalytics) Close() error {
|
|
err := ta.consumer.Close()
|
|
tdLogInfo("SDK close")
|
|
return err
|
|
}
|
|
|
|
func (ta *TDAnalytics) add(accountId, distinctId, dataType, eventName, eventId string, properties map[string]interface{}) error {
|
|
if len(accountId) == 0 && len(distinctId) == 0 {
|
|
msg := "invalid parameters: account_id and distinct_id cannot be empty at the same time"
|
|
tdLogError(msg)
|
|
return errors.New(msg)
|
|
}
|
|
|
|
// get "#ip" value in properties, empty string will be return when not found.
|
|
ip := extractStringProperty(properties, "#ip")
|
|
|
|
// get "#app_id" value in properties, empty string will be return when not found.
|
|
appId := extractStringProperty(properties, "#app_id")
|
|
|
|
// get "#time" value in properties, empty string will be return when not found.
|
|
eventTime := extractTime(properties)
|
|
|
|
firstCheckId := extractStringProperty(properties, "#first_check_id")
|
|
|
|
// get "#uuid" value in properties, empty string will be return when not found.
|
|
uuid := extractStringProperty(properties, "#uuid")
|
|
if len(uuid) == 0 {
|
|
uuid = generateUUID()
|
|
}
|
|
properties_summary, err := json.Marshal(properties)
|
|
if err != nil {
|
|
properties_summary = []byte{}
|
|
}
|
|
data := Data{
|
|
AccountId: accountId,
|
|
DistinctId: distinctId,
|
|
Type: dataType,
|
|
Time: eventTime,
|
|
Timestamp: time.Now().Unix(),
|
|
EventName: eventName,
|
|
EventId: eventId,
|
|
FirstCheckId: firstCheckId,
|
|
Ip: ip,
|
|
UUID: uuid,
|
|
Properties: properties,
|
|
PropertiesSummary: string(properties_summary),
|
|
}
|
|
|
|
if len(appId) > 0 {
|
|
data.AppId = appId
|
|
}
|
|
|
|
err = formatProperties(&data, ta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return ta.consumer.Add(data)
|
|
}
|
|
|
|
// Deprecated: please use TDConsumer
|
|
type Consumer interface {
|
|
TDConsumer
|
|
}
|