/* Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments Copyright (C) ITsysCOM GmbH This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see */ package engine import ( "sync" "time" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" ) func NewSafEvent(mp map[string]any) *SafEvent { return &SafEvent{Me: NewMapEvent(mp)} } // SafEvent is a generic event which is safe to read/write from multiple goroutines type SafEvent struct { sync.RWMutex Me MapEvent // need it exportable so we can pass it on network } func (se *SafEvent) Clone() (cln *SafEvent) { se.RLock() cln = &SafEvent{Me: se.Me.Clone()} se.RUnlock() return } // MapEvent offers access to MapEvent methods, avoiding locks func (se *SafEvent) MapEvent() (mp MapEvent) { return se.Me } func (se *SafEvent) String() (out string) { se.RLock() out = se.Me.String() se.RUnlock() return } func (se *SafEvent) FieldAsInterface(fldPath []string) (out any, err error) { se.RLock() out, err = se.Me.FieldAsInterface(fldPath) se.RUnlock() return } func (se *SafEvent) FieldAsString(fldPath []string) (out string, err error) { se.RLock() out, err = se.Me.FieldAsString(fldPath) se.RUnlock() return } func (se *SafEvent) HasField(fldName string) (has bool) { se.RLock() has = se.Me.HasField(fldName) se.RUnlock() return } func (se *SafEvent) Get(fldName string) (out any, has bool) { se.RLock() out, has = se.Me[fldName] se.RUnlock() return } func (se *SafEvent) GetIgnoreErrors(fldName string) (out any) { out, _ = se.Get(fldName) return } // Set will set a field's value func (se *SafEvent) Set(fldName string, val any) { se.Lock() se.Me[fldName] = val se.Unlock() } // Remove will remove a field from map func (se *SafEvent) Remove(fldName string) { se.Lock() delete(se.Me, fldName) se.Unlock() } func (se *SafEvent) GetString(fldName string) (out string, err error) { se.RLock() out, err = se.Me.GetString(fldName) se.RUnlock() return } func (se *SafEvent) GetTInt64(fldName string) (out int64, err error) { se.RLock() out, err = se.Me.GetTInt64(fldName) se.RUnlock() return } func (se *SafEvent) GetStringIgnoreErrors(fldName string) (out string) { out, _ = se.GetString(fldName) return } // GetDuration returns a field as Duration func (se *SafEvent) GetDuration(fldName string) (d time.Duration, err error) { se.RLock() d, err = se.Me.GetDuration(fldName) se.RUnlock() return } // GetDurationPointer returns pointer towards duration, useful to detect presence of duration func (se *SafEvent) GetDurationOrDefault(fldName string, dflt time.Duration) (d time.Duration, err error) { _, has := se.Get(fldName) if !has { return dflt, nil } return se.GetDuration(fldName) } // GetDuration returns a field as Duration, ignoring errors func (se *SafEvent) GetDurationIgnoreErrors(fldName string) (d time.Duration) { d, _ = se.GetDuration(fldName) return } // GetDurationPointer returns pointer towards duration, useful to detect presence of duration func (se *SafEvent) GetDurationPtr(fldName string) (d *time.Duration, err error) { fldIface, has := se.Get(fldName) if !has { return nil, utils.ErrNotFound } var dReal time.Duration if dReal, err = utils.IfaceAsDuration(fldIface); err != nil { return } return &dReal, nil } // GetDurationPointer returns pointer towards duration, useful to detect presence of duration func (se *SafEvent) GetDurationPtrIgnoreErrors(fldName string) (d *time.Duration) { d, _ = se.GetDurationPtr(fldName) return } // GetDurationPointer returns pointer towards duration, useful to detect presence of duration func (se *SafEvent) GetDurationPtrOrDefault(fldName string, dflt *time.Duration) (d *time.Duration, err error) { fldIface, has := se.Get(fldName) if !has { return dflt, nil } var dReal time.Duration if dReal, err = utils.IfaceAsDuration(fldIface); err != nil { return } return &dReal, nil } // GetTime returns a field as Time func (se *SafEvent) GetTime(fldName string, tmz string) (t time.Time, err error) { se.RLock() t, err = se.Me.GetTime(fldName, tmz) se.RUnlock() return } // GetTimeIgnoreErrors returns a field as Time instance, ignoring errors func (se *SafEvent) GetTimeIgnoreErrors(fldName string, tmz string) (t time.Time) { t, _ = se.GetTime(fldName, tmz) return } // GetSet will attempt to get a field value // if field not present set it to the value received as parameter func (se *SafEvent) GetSetString(fldName string, setVal string) (out string, err error) { se.Lock() defer se.Unlock() outIface, has := se.Me[fldName] if !has { se.Me[fldName] = setVal out = setVal return } // should be present, return it as string return utils.IfaceAsString(outIface), nil } // GetMapInterface returns the map stored internally without cloning it func (se *SafEvent) GetMapInterface() (mp map[string]any) { se.RLock() mp = se.Me se.RUnlock() return } // AsMapInterface returns the cloned map stored internally func (se *SafEvent) AsMapInterface() (mp map[string]any) { se.RLock() mp = se.Me.Clone() se.RUnlock() return } // AsMapString returns a map[string]string out of mp, ignoring specific fields if needed // most used when needing to export extraFields func (se *SafEvent) AsMapString(ignoredFlds utils.StringSet) (mp map[string]string) { se.RLock() mp = se.Me.AsMapString(ignoredFlds) se.RUnlock() return } // AsCDR exports the SafEvent as CDR func (se *SafEvent) AsCDR(cfg *config.CGRConfig, tnt, tmz string) (cdr *CDR, err error) { se.RLock() cdr, err = se.Me.AsCDR(cfg, tnt, tmz) se.RUnlock() return } // AsCGREvent exports the SafEvent as CGREvent func (se *SafEvent) AsCGREvent(tnt string) (cgrEv *utils.CGREvent) { se.RLock() cgrEv = &utils.CGREvent{ Tenant: tnt, ID: utils.UUIDSha1Prefix(), Time: utils.TimePointer(time.Now()), Event: se.Me.Data(), } se.RUnlock() return }