From c1e399de6db7fcbee003acab97b7ccd6efc9440a Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 6 Dec 2021 17:07:44 +0200 Subject: [PATCH] Added all merge functions --- engine/actionprofile.go | 22 +++ engine/dispatcherprfl.go | 61 +++++++ engine/libchargers.go | 18 ++ loaders/libloader.go | 169 +++++++++++-------- loaders/libloader_test.go | 67 +++++--- loaders/loader.go | 323 +++++++----------------------------- loaders/loader_test.go | 283 ++++++++----------------------- utils/account.go | 19 +++ utils/dataconverter_test.go | 4 +- utils/librates.go | 25 +++ 10 files changed, 416 insertions(+), 575 deletions(-) diff --git a/engine/actionprofile.go b/engine/actionprofile.go index 196444a7a..7b90d25d9 100644 --- a/engine/actionprofile.go +++ b/engine/actionprofile.go @@ -204,3 +204,25 @@ func (aP *APAction) Set(path []string, val interface{}, newBranch bool) (err err } return } + +func (ap *ActionProfile) Merge(v2 interface{}) { + vi := v2.(*ActionProfile) + if len(vi.Tenant) != 0 { + ap.Tenant = vi.Tenant + } + if len(vi.ID) != 0 { + ap.ID = vi.ID + } + ap.FilterIDs = append(ap.FilterIDs, vi.FilterIDs...) + ap.Actions = append(ap.Actions, vi.Actions...) + + if vi.Weight != 0 { + ap.Weight = vi.Weight + } + if len(vi.Schedule) != 0 { + ap.Schedule = vi.Schedule + } + for k, v := range vi.Targets { + ap.Targets[k] = v + } +} diff --git a/engine/dispatcherprfl.go b/engine/dispatcherprfl.go index 56e39a18c..b8773e4a4 100644 --- a/engine/dispatcherprfl.go +++ b/engine/dispatcherprfl.go @@ -296,3 +296,64 @@ func (dH *DispatcherHost) Set(path []string, val interface{}, newBranch bool, _ } return } + +func (dP *DispatcherProfile) Merge(v2 interface{}) { + vi := v2.(*DispatcherProfile) + if len(vi.Tenant) != 0 { + dP.Tenant = vi.Tenant + } + if len(vi.ID) != 0 { + dP.ID = vi.ID + } + dP.FilterIDs = append(dP.FilterIDs, vi.FilterIDs...) + dP.Hosts = append(dP.Hosts, vi.Hosts...) + if vi.Weight != 0 { + dP.Weight = vi.Weight + } + for k, v := range vi.StrategyParams { + dP.StrategyParams[k] = v + } + if len(vi.Strategy) != 0 { + dP.Strategy = vi.Strategy + } +} + +func (dH *DispatcherHost) Merge(v2 interface{}) { + vi := v2.(*DispatcherHost) + if len(vi.Tenant) != 0 { + dH.Tenant = vi.Tenant + } + if len(vi.ID) != 0 { + dH.ID = vi.ID + } + if len(vi.Address) != 0 { + dH.Address = vi.Address + } + if len(vi.Transport) != 0 { + dH.Transport = vi.Transport + } + if len(vi.ClientKey) != 0 { + dH.ClientKey = vi.ClientKey + } + if len(vi.ClientCertificate) != 0 { + dH.ClientCertificate = vi.ClientCertificate + } + if len(vi.CaCertificate) != 0 { + dH.CaCertificate = vi.CaCertificate + } + if vi.TLS { + dH.TLS = vi.TLS + } + if vi.ConnectTimeout != 0 { + dH.ConnectTimeout = vi.ConnectTimeout + } + if vi.ReplyTimeout != 0 { + dH.ReplyTimeout = vi.ReplyTimeout + } + if vi.ConnectAttempts != 0 { + dH.ConnectAttempts = vi.ConnectAttempts + } + if vi.Reconnects != 0 { + dH.Reconnects = vi.Reconnects + } +} diff --git a/engine/libchargers.go b/engine/libchargers.go index 4ac7c016b..6ddeab6da 100644 --- a/engine/libchargers.go +++ b/engine/libchargers.go @@ -78,3 +78,21 @@ func (cp *ChargerProfile) Set(path []string, val interface{}, newBranch bool, _ } return } + +func (cp *ChargerProfile) Merge(v2 interface{}) { + vi := v2.(*ChargerProfile) + if len(vi.Tenant) != 0 { + cp.Tenant = vi.Tenant + } + if len(vi.ID) != 0 { + cp.ID = vi.ID + } + if len(vi.RunID) != 0 { + cp.RunID = vi.RunID + } + cp.FilterIDs = append(cp.FilterIDs, vi.FilterIDs...) + cp.AttributeIDs = append(cp.AttributeIDs, vi.AttributeIDs...) + if vi.Weight != 0 { + cp.Weight = vi.Weight + } +} diff --git a/loaders/libloader.go b/loaders/libloader.go index c2cabb38c..000067392 100644 --- a/loaders/libloader.go +++ b/loaders/libloader.go @@ -31,25 +31,22 @@ import ( // UpdateFromCSV will update LoaderData with data received from fileName, // contained in record and processed with cfgTpl -func newRecord(ctx *context.Context, req utils.DataProvider, tmpls []*config.FCTemplate, tnt string, filterS *engine.FilterS, - cfg *config.CGRConfig, cache *ltcache.Cache) (_ *utils.OrderedNavigableMap, err error) { - r := &record{ - data: utils.NewOrderedNavigableMap(), +func newRecord(req utils.DataProvider, data profile, tnt string, cfg *config.CGRConfig, cache *ltcache.Cache) *record { + return &record{ + data: data, + dp: config.NewObjectDP(data), tmp: &utils.DataNode{Type: utils.NMMapType, Map: make(map[string]*utils.DataNode)}, req: req, cfg: cfg.GetDataProvider(), cache: cache, tenant: tnt, } - if err = r.SetFields(ctx, tmpls, filterS, cfg.GeneralCfg().RoundingDecimals, cfg.GeneralCfg().DefaultTimezone, cfg.GeneralCfg().RSRSep); err != nil { - return - } - return r.data, nil } type record struct { tenant string - data *utils.OrderedNavigableMap + dp utils.DataProvider + data profile tmp *utils.DataNode req utils.DataProvider cfg utils.DataProvider @@ -87,7 +84,7 @@ func RateIDsFromOrderedNavigableMap(data *utils.OrderedNavigableMap) ([]string, func (ar *record) FieldAsInterface(fldPath []string) (val interface{}, err error) { switch fldPath[0] { default: - val, err = ar.data.FieldAsInterface(fldPath) + val, err = ar.dp.FieldAsInterface(fldPath) case utils.MetaReq: if len(fldPath) != 1 { val, err = ar.req.FieldAsInterface(fldPath[1:]) @@ -128,7 +125,6 @@ func (ar *record) FieldAsInterface(fldPath []string) (val interface{}, err error } //SetFields will populate fields of record out of templates - func (ar *record) SetFields(ctx *context.Context, tmpls []*config.FCTemplate, filterS *engine.FilterS, rndDec int, dftTmz, rsrSep string) (err error) { ar.tmp = &utils.DataNode{Type: utils.NMMapType, Map: make(map[string]*utils.DataNode)} for _, fld := range tmpls { @@ -177,13 +173,9 @@ func (ar *record) SetFields(ctx *context.Context, tmpls []*config.FCTemplate, fi nMItm := &utils.DataLeaf{Data: out, NewBranch: fld.NewBranch, AttributeID: fld.AttributeID} switch fld.Type { case utils.MetaComposed: - err = ar.Compose(fullPath, nMItm) - case utils.MetaGroup: // in case of *group type simply append to valSet - err = ar.Append(fullPath, nMItm) - case utils.MetaGeneric: - err = ar.Set(fullPath, out) + err = ar.Compose(fullPath, nMItm, rsrSep) default: - err = ar.SetAsSlice(fullPath, nMItm) + err = ar.Set(fullPath, nMItm, rsrSep) } if err != nil { return @@ -196,25 +188,11 @@ func (ar *record) SetFields(ctx *context.Context, tmpls []*config.FCTemplate, fi return } -// Set implements utils.NMInterface -func (ar *record) SetAsSlice(fullPath *utils.FullPath, nm *utils.DataLeaf) (err error) { - switch fullPath.PathSlice[0] { - default: - return ar.data.SetAsSlice(fullPath, []*utils.DataNode{{Type: utils.NMDataType, Value: nm}}) - case utils.MetaTmp: - _, err = ar.tmp.Set(fullPath.PathSlice[1:], []*utils.DataNode{{Type: utils.NMDataType, Value: nm}}) - return - case utils.MetaUCH: - ar.cache.Set(fullPath.Path[5:], nm.Data, nil) - return - } -} - // RemoveAll deletes all fields at given prefix func (ar *record) RemoveAll(prefix string) error { switch prefix { default: - ar.data = utils.NewOrderedNavigableMap() + // ar.data = utils.NewOrderedNavigableMap() case utils.MetaTmp: ar.tmp = &utils.DataNode{Type: utils.NMMapType, Map: make(map[string]*utils.DataNode)} case utils.MetaUCH: @@ -227,10 +205,10 @@ func (ar *record) RemoveAll(prefix string) error { func (ar *record) Remove(fullPath *utils.FullPath) error { switch fullPath.PathSlice[0] { default: - return ar.data.Remove(&utils.FullPath{ + return nil /* ar.data.Remove(&utils.FullPath{ PathSlice: fullPath.PathSlice, Path: fullPath.Path, - }) + })*/ case utils.MetaTmp: return ar.tmp.Remove(utils.CloneStringSlice(fullPath.PathSlice[1:])) case utils.MetaUCH: @@ -239,24 +217,9 @@ func (ar *record) Remove(fullPath *utils.FullPath) error { } } -// Append sets the value at the given path -// this used with full path and the processed path to not calculate them for every set -func (ar *record) Append(fullPath *utils.FullPath, val *utils.DataLeaf) (err error) { - switch fullPath.PathSlice[0] { - case utils.MetaTmp: - _, err = ar.tmp.Append(fullPath.PathSlice[1:], val) - return - case utils.MetaUCH: - ar.cache.Set(fullPath.Path[5:], val.Data, nil) - return - default: - return ar.data.Append(fullPath, val) - } -} - // Set sets the value at the given path // this used with full path and the processed path to not calculate them for every set -func (ar *record) Compose(fullPath *utils.FullPath, val *utils.DataLeaf) (err error) { +func (ar *record) Compose(fullPath *utils.FullPath, val *utils.DataLeaf, sep string) (err error) { switch fullPath.PathSlice[0] { case utils.MetaTmp: return ar.tmp.Compose(fullPath.PathSlice[1:], val) @@ -271,36 +234,19 @@ func (ar *record) Compose(fullPath *utils.FullPath, val *utils.DataLeaf) (err er ar.cache.Set(path, prv, nil) return default: - return ar.data.Compose(fullPath, val) - } -} - -type profile interface { - Set([]string, interface{}, bool, string) error -} - -func prepareData(prf profile, lData []*utils.OrderedNavigableMap, rsrSep string) (err error) { - for _, mp := range lData { - for el := mp.GetFirstElement(); el != nil; el = el.Next() { - path := el.Value - nmIt, _ := mp.Field(path) - if nmIt == nil { - continue - } - path = path[:len(path)-1] // remove the last index - if err = prf.Set(path, nmIt.Data, nmIt.NewBranch, rsrSep); err != nil { - return - } + var valStr string + if valStr, err = ar.FieldAsString(fullPath.PathSlice); err != nil && err != utils.ErrNotFound { + return } + return ar.data.Set(fullPath.PathSlice, valStr+utils.IfaceAsString(val.Data), val.NewBranch, utils.InfieldSep) } - return } // Set implements utils.NMInterface -func (ar *record) Set(fullPath *utils.FullPath, nm interface{}) (err error) { +func (ar *record) Set(fullPath *utils.FullPath, nm *utils.DataLeaf, sep string) (err error) { switch fullPath.PathSlice[0] { default: - return ar.data.Set(fullPath, nm) + return ar.data.Set(fullPath.PathSlice, nm.Data, nm.NewBranch, sep) case utils.MetaTmp: _, err = ar.tmp.Set(fullPath.PathSlice[1:], nm) return @@ -309,3 +255,80 @@ func (ar *record) Set(fullPath *utils.FullPath, nm interface{}) (err error) { return } } + +type profile interface { + // utils.DataProvider + Set([]string, interface{}, bool, string) error + Merge(interface{}) + TenantID() string +} + +func newProfileFunc(lType string) func() profile { + switch lType { + case utils.MetaAttributes: + return func() profile { + return new(engine.AttributeProfile) + } + case utils.MetaResources: + return func() profile { + return new(engine.ResourceProfile) + } + case utils.MetaFilters: + return func() profile { + return new(engine.Filter) + } + case utils.MetaStats: + return func() profile { + return new(engine.StatQueueProfile) + } + case utils.MetaThresholds: + return func() profile { + return new(engine.ThresholdProfile) + } + case utils.MetaRoutes: + return func() profile { + return new(engine.RouteProfile) + } + case utils.MetaChargers: + return func() profile { + return new(engine.ChargerProfile) + } + case utils.MetaDispatchers: + return func() profile { + return &engine.DispatcherProfile{ + StrategyParams: make(map[string]interface{}), + } + } + case utils.MetaDispatcherHosts: + return func() profile { + return &engine.DispatcherHost{ + RemoteHost: &config.RemoteHost{ + Transport: utils.MetaJSON, + }, + } + } + case utils.MetaRateProfiles: + return func() profile { + return &utils.RateProfile{ + Rates: make(map[string]*utils.Rate), + MinCost: utils.NewDecimal(0, 0), + MaxCost: utils.NewDecimal(0, 0), + } + } + case utils.MetaActionProfiles: + return func() profile { + return &engine.ActionProfile{ + Targets: make(map[string]utils.StringSet), + } + } + case utils.MetaAccounts: + return func() profile { + return &utils.Account{ + Opts: make(map[string]interface{}), + Balances: make(map[string]*utils.Balance), + } + } + default: + return func() profile { return nil } + } +} diff --git a/loaders/libloader_test.go b/loaders/libloader_test.go index 9d3638d41..46b97d94c 100644 --- a/loaders/libloader_test.go +++ b/loaders/libloader_test.go @@ -19,6 +19,7 @@ along with this program. If not, see package loaders import ( + "encoding/json" "reflect" "testing" @@ -62,21 +63,20 @@ func TestNewRecord(t *testing.T) { cfg := config.NewDefaultCGRConfig() fs := engine.NewFilterS(cfg, nil, nil) expErrMsg := "inline parse error for string: <*string>" - if _, err := newRecord(context.Background(), utils.MapStorage{}, - []*config.FCTemplate{ + if err := newRecord(utils.MapStorage{}, nil, "cgrates.org", cfg, ltcache.NewCache(-1, 0, false, nil)). + SetFields(context.Background(), []*config.FCTemplate{ {Filters: []string{"*exists:~*req.NoField:"}}, {Filters: []string{"*string"}}, - }, - "cgrates.org", fs, cfg, ltcache.NewCache(-1, 0, false, nil)); err == nil || err.Error() != expErrMsg { + }, fs, 0, "", utils.InfieldSep); err == nil || err.Error() != expErrMsg { t.Errorf("Expeceted: %q, received: %v", expErrMsg, err) } - r, err := newRecord(context.Background(), utils.MapStorage{}, []*config.FCTemplate{}, "cgrates.org", fs, cfg, ltcache.NewCache(-1, 0, false, nil)) - if err != nil { - t.Fatal(err) + pt := profileTest{} + if err := newRecord(utils.MapStorage{}, pt, "cgrates.org", cfg, ltcache.NewCache(-1, 0, false, nil)). + SetFields(context.Background(), []*config.FCTemplate{}, fs, 0, "", utils.InfieldSep); err != nil { + t.Error(err) } - exp := utils.NewOrderedNavigableMap() - if !reflect.DeepEqual(r, exp) { - t.Errorf("Expected %+v, received %+q", exp, r) + if len(pt) != 0 { + t.Fatal("Expected empty map") } } @@ -92,16 +92,17 @@ func TestNewRecordWithCahe(t *testing.T) { for _, f := range fc { f.ComputePath() } - exp := utils.NewOrderedNavigableMap() - exp.SetAsSlice(utils.NewFullPath(utils.Tenant), []*utils.DataNode{{Type: utils.NMDataType, Value: &utils.DataLeaf{Data: "cgrates.org"}}}) - exp.SetAsSlice(utils.NewFullPath(utils.ID), []*utils.DataNode{{Type: utils.NMDataType, Value: &utils.DataLeaf{Data: "Attr1"}}}) - exp.SetAsSlice(utils.NewFullPath(utils.Value), []*utils.DataNode{{Type: utils.NMDataType, Value: &utils.DataLeaf{Data: "0"}}}) - if r, err := newRecord(context.Background(), config.NewSliceDP([]string{"cgrates.org", "Attr1"}, nil), - fc, "cgrates.org", fs, cfg, ltcache.NewCache(-1, 0, false, nil)); err != nil { + exp := profileTest{ + utils.Tenant: "cgrates.org", + utils.ID: "Attr1", + utils.Value: "0", + } + r := profileTest{} + if err := newRecord(config.NewSliceDP([]string{"cgrates.org", "Attr1"}, nil), r, "cgrates.org", cfg, ltcache.NewCache(-1, 0, false, nil)). + SetFields(context.Background(), fc, fs, 0, "", utils.InfieldSep); err != nil { t.Error(err) } else if !reflect.DeepEqual(r, exp) { t.Errorf("Expected %+v, received %+v", exp, r) - t.Errorf("Expected %+v, received %+v", utils.ToJSON(exp.GetOrder()), utils.ToJSON(r.GetOrder())) } } @@ -117,14 +118,32 @@ func TestNewRecordWithTmp(t *testing.T) { for _, f := range fc { f.ComputePath() } - exp := utils.NewOrderedNavigableMap() - exp.SetAsSlice(utils.NewFullPath(utils.Tenant), []*utils.DataNode{{Type: utils.NMDataType, Value: &utils.DataLeaf{Data: "cgrates.org"}}}) - exp.SetAsSlice(utils.NewFullPath(utils.ID), []*utils.DataNode{{Type: utils.NMDataType, Value: &utils.DataLeaf{Data: "Attr1"}}}) - exp.SetAsSlice(utils.NewFullPath(utils.Value), []*utils.DataNode{{Type: utils.NMDataType, Value: &utils.DataLeaf{Data: "0"}}}) - if r, err := newRecord(context.Background(), config.NewSliceDP([]string{"cgrates.org", "Attr1"}, nil), - fc, "cgrates.org", fs, cfg, ltcache.NewCache(-1, 0, false, nil)); err != nil { + exp := profileTest{ + utils.Tenant: "cgrates.org", + utils.ID: "Attr1", + utils.Value: "0", + } + r := profileTest{} + if err := newRecord(config.NewSliceDP([]string{"cgrates.org", "Attr1"}, nil), r, "cgrates.org", cfg, ltcache.NewCache(-1, 0, false, nil)). + SetFields(context.Background(), fc, fs, 0, "", utils.InfieldSep); err != nil { t.Error(err) } else if !reflect.DeepEqual(r, exp) { - t.Errorf("Expected %+v, received %+q", exp, r) + t.Errorf("Expected %+v, received %+v", exp, r) } } + +type profileTest utils.MapStorage + +func (p profileTest) Set(path []string, val interface{}, _ bool, _ string) error { + return utils.MapStorage(p).Set(path, val) +} +func (p profileTest) Merge(v2 interface{}) { + var vi map[string]interface{} + json.Unmarshal([]byte(utils.ToJSON(v2)), &vi) + for k, v := range vi { + (map[string]interface{}(p))[k] = v + } +} +func (p profileTest) TenantID() string { + return utils.ConcatenatedKey(utils.IfaceAsString(map[string]interface{}(p)[utils.Tenant]), utils.IfaceAsString(map[string]interface{}(p)[utils.ID])) +} diff --git a/loaders/loader.go b/loaders/loader.go index 957271025..02bd4f98e 100644 --- a/loaders/loader.go +++ b/loaders/loader.go @@ -32,11 +32,13 @@ import ( "github.com/cgrates/ltcache" ) -const ( - gprefix = utils.MetaGoogleAPI + utils.ConcatenatedKeySep -) +// const ( +// gprefix = utils.MetaGoogleAPI + utils.ConcatenatedKeySep +// ) -func removeFromDB(ctx *context.Context, dm *engine.DataManager, lType, tnt, id string, withIndex, ratesPartial bool, ratesData *utils.OrderedNavigableMap) (_ error) { +func removeFromDB(ctx *context.Context, dm *engine.DataManager, lType string, withIndex, ratesPartial bool, obj profile) (_ error) { + tntID := utils.NewTenantID(obj.TenantID()) + tnt, id := tntID.Tenant, tntID.ID switch lType { case utils.MetaAttributes: return dm.RemoveAttributeProfile(ctx, tnt, id, withIndex) @@ -58,11 +60,12 @@ func removeFromDB(ctx *context.Context, dm *engine.DataManager, lType, tnt, id s return dm.RemoveDispatcherHost(ctx, tnt, id) case utils.MetaRateProfiles: if ratesPartial { - rateIDs, err := RateIDsFromOrderedNavigableMap(ratesData) - if err != nil { - return err + rt := obj.(*utils.RateProfile) + ids := make([]string, 0, len(rt.Rates)) + for k := range rt.Rates { + ids = append(ids, k) } - return dm.RemoveRateProfileRates(ctx, tnt, id, rateIDs, withIndex) + return dm.RemoveRateProfileRates(ctx, tnt, id, ids, withIndex) } return dm.RemoveRateProfile(ctx, tnt, id, withIndex) case utils.MetaActionProfiles: @@ -73,108 +76,33 @@ func removeFromDB(ctx *context.Context, dm *engine.DataManager, lType, tnt, id s return } -func setToDB(ctx *context.Context, dm *engine.DataManager, lType, rsrSep string, tntID *utils.TenantID, lDataSet []*utils.OrderedNavigableMap, withIndex, ratesPartial bool) (err error) { +func setToDB(ctx *context.Context, dm *engine.DataManager, lType string, data profile, withIndex, ratesPartial bool) (err error) { switch lType { case utils.MetaAttributes: - apf := &engine.AttributeProfile{ - Tenant: tntID.Tenant, - ID: tntID.ID, - } - if err = prepareData(apf, lDataSet, rsrSep); err != nil { - return - } - return dm.SetAttributeProfile(ctx, apf, withIndex) + return dm.SetAttributeProfile(ctx, data.(*engine.AttributeProfile), withIndex) case utils.MetaResources: - res := &engine.ResourceProfile{ - Tenant: tntID.Tenant, - ID: tntID.ID, - } - if err = prepareData(res, lDataSet, rsrSep); err != nil { - return - } - return dm.SetResourceProfile(ctx, res, withIndex) + return dm.SetResourceProfile(ctx, data.(*engine.ResourceProfile), withIndex) case utils.MetaFilters: - fltr := &engine.Filter{ - Tenant: tntID.Tenant, - ID: tntID.ID, - } - if err = prepareData(fltr, lDataSet, rsrSep); err != nil { - return - } + fltr := data.(*engine.Filter) fltr.Compress() if err = fltr.Compile(); err != nil { return } return dm.SetFilter(ctx, fltr, withIndex) case utils.MetaStats: - stsPrf := &engine.StatQueueProfile{ - Tenant: tntID.Tenant, - ID: tntID.ID, - } - if err = prepareData(stsPrf, lDataSet, rsrSep); err != nil { - return - } - return dm.SetStatQueueProfile(ctx, stsPrf, withIndex) + return dm.SetStatQueueProfile(ctx, data.(*engine.StatQueueProfile), withIndex) case utils.MetaThresholds: - thPrf := &engine.ThresholdProfile{ - Tenant: tntID.Tenant, - ID: tntID.ID, - } - if err = prepareData(thPrf, lDataSet, rsrSep); err != nil { - return - } - return dm.SetThresholdProfile(ctx, thPrf, withIndex) + return dm.SetThresholdProfile(ctx, data.(*engine.ThresholdProfile), withIndex) case utils.MetaRoutes: - spPrf := &engine.RouteProfile{ - Tenant: tntID.Tenant, - ID: tntID.ID, - } - if err = prepareData(spPrf, lDataSet, rsrSep); err != nil { - return - } - return dm.SetRouteProfile(ctx, spPrf, withIndex) + return dm.SetRouteProfile(ctx, data.(*engine.RouteProfile), withIndex) case utils.MetaChargers: - cpPrf := &engine.ChargerProfile{ - Tenant: tntID.Tenant, - ID: tntID.ID, - } - if err = prepareData(cpPrf, lDataSet, rsrSep); err != nil { - return - } - return dm.SetChargerProfile(ctx, cpPrf, withIndex) + return dm.SetChargerProfile(ctx, data.(*engine.ChargerProfile), withIndex) case utils.MetaDispatchers: - dpPrf := &engine.DispatcherProfile{ - Tenant: tntID.Tenant, - ID: tntID.ID, - StrategyParams: make(map[string]interface{}), - } - if err = prepareData(dpPrf, lDataSet, rsrSep); err != nil { - return - } - return dm.SetDispatcherProfile(ctx, dpPrf, withIndex) + return dm.SetDispatcherProfile(ctx, data.(*engine.DispatcherProfile), withIndex) case utils.MetaDispatcherHosts: - dpPrf := &engine.DispatcherHost{ - Tenant: tntID.Tenant, - RemoteHost: &config.RemoteHost{ - ID: tntID.ID, - Transport: utils.MetaJSON, - }, - } - if err = prepareData(dpPrf, lDataSet, rsrSep); err != nil { - return - } - return dm.SetDispatcherHost(ctx, dpPrf) + return dm.SetDispatcherHost(ctx, data.(*engine.DispatcherHost)) case utils.MetaRateProfiles: - rpl := &utils.RateProfile{ - Tenant: tntID.Tenant, - ID: tntID.ID, - Rates: make(map[string]*utils.Rate), - MinCost: utils.NewDecimal(0, 0), - MaxCost: utils.NewDecimal(0, 0), - } - if err = prepareData(rpl, lDataSet, rsrSep); err != nil { - return - } + rpl := data.(*utils.RateProfile) if ratesPartial { err = dm.SetRateProfileRates(ctx, rpl, true) } else { @@ -182,180 +110,48 @@ func setToDB(ctx *context.Context, dm *engine.DataManager, lType, rsrSep string, } return case utils.MetaActionProfiles: - acp := &engine.ActionProfile{ - Tenant: tntID.Tenant, - ID: tntID.ID, - Targets: make(map[string]utils.StringSet), - } - if err = prepareData(acp, lDataSet, rsrSep); err != nil { - return - } - return dm.SetActionProfile(ctx, acp, withIndex) + return dm.SetActionProfile(ctx, data.(*engine.ActionProfile), withIndex) case utils.MetaAccounts: - acp := &utils.Account{ - Tenant: tntID.Tenant, - ID: tntID.ID, - Opts: make(map[string]interface{}), - Balances: make(map[string]*utils.Balance), - } - if err = prepareData(acp, lDataSet, rsrSep); err != nil { - return - } - return dm.SetAccount(ctx, acp, withIndex) + return dm.SetAccount(ctx, data.(*utils.Account), withIndex) } return } -func dryRun(ctx *context.Context, lType, rsrSep, ldrID string, tntID *utils.TenantID, lDataSet []*utils.OrderedNavigableMap) (err error) { +func dryRun(ctx *context.Context, lType, ldrID string, obj profile) (err error) { + var msg string switch lType { case utils.MetaAttributes: - apf := &engine.AttributeProfile{ - Tenant: tntID.Tenant, - ID: tntID.ID, - } - if err = prepareData(apf, lDataSet, rsrSep); err != nil { - return - } - utils.Logger.Info( - fmt.Sprintf("<%s-%s> DRY_RUN: AttributeProfile: %s", - utils.LoaderS, ldrID, utils.ToJSON(apf))) + msg = "<%s-%s> DRY_RUN: AttributeProfile: %s" case utils.MetaResources: - res := &engine.ResourceProfile{ - Tenant: tntID.Tenant, - ID: tntID.ID, - } - if err = prepareData(res, lDataSet, rsrSep); err != nil { - return - } - utils.Logger.Info( - fmt.Sprintf("<%s-%s> DRY_RUN: ResourceProfile: %s", - utils.LoaderS, ldrID, utils.ToJSON(res))) + msg = "<%s-%s> DRY_RUN: ResourceProfile: %s" case utils.MetaFilters: - fltr := &engine.Filter{ - Tenant: tntID.Tenant, - ID: tntID.ID, - } - if err = prepareData(fltr, lDataSet, rsrSep); err != nil { - return - } + fltr := obj.(*engine.Filter) fltr.Compress() if err = fltr.Compile(); err != nil { return } - utils.Logger.Info( - fmt.Sprintf("<%s-%s> DRY_RUN: Filter: %s", - utils.LoaderS, ldrID, utils.ToJSON(fltr))) + msg = "<%s-%s> DRY_RUN: Filter: %s" case utils.MetaStats: - stsPrf := &engine.StatQueueProfile{ - Tenant: tntID.Tenant, - ID: tntID.ID, - } - if err = prepareData(stsPrf, lDataSet, rsrSep); err != nil { - return - } - utils.Logger.Info( - fmt.Sprintf("<%s-%s> DRY_RUN: StatsQueueProfile: %s", - utils.LoaderS, ldrID, utils.ToJSON(stsPrf))) + msg = "<%s-%s> DRY_RUN: StatsQueueProfile: %s" case utils.MetaThresholds: - thPrf := &engine.ThresholdProfile{ - Tenant: tntID.Tenant, - ID: tntID.ID, - } - if err = prepareData(thPrf, lDataSet, rsrSep); err != nil { - return - } - utils.Logger.Info( - fmt.Sprintf("<%s-%s> DRY_RUN: ThresholdProfile: %s", - utils.LoaderS, ldrID, utils.ToJSON(thPrf))) + msg = "<%s-%s> DRY_RUN: ThresholdProfile: %s" case utils.MetaRoutes: - spPrf := &engine.RouteProfile{ - Tenant: tntID.Tenant, - ID: tntID.ID, - } - if err = prepareData(spPrf, lDataSet, rsrSep); err != nil { - return - } - utils.Logger.Info( - fmt.Sprintf("<%s-%s> DRY_RUN: RouteProfile: %s", - utils.LoaderS, ldrID, utils.ToJSON(spPrf))) + msg = "<%s-%s> DRY_RUN: RouteProfile: %s" case utils.MetaChargers: - cpPrf := &engine.ChargerProfile{ - Tenant: tntID.Tenant, - ID: tntID.ID, - } - if err = prepareData(cpPrf, lDataSet, rsrSep); err != nil { - return - } - utils.Logger.Info( - fmt.Sprintf("<%s-%s> DRY_RUN: ChargerProfile: %s", - utils.LoaderS, ldrID, utils.ToJSON(cpPrf))) + msg = "<%s-%s> DRY_RUN: ChargerProfile: %s" case utils.MetaDispatchers: - dpPrf := &engine.DispatcherProfile{ - Tenant: tntID.Tenant, - ID: tntID.ID, - StrategyParams: make(map[string]interface{}), - } - if err = prepareData(dpPrf, lDataSet, rsrSep); err != nil { - return - } - utils.Logger.Info( - fmt.Sprintf("<%s-%s> DRY_RUN: DispatcherProfile: %s", - utils.LoaderS, ldrID, utils.ToJSON(dpPrf))) + msg = "<%s-%s> DRY_RUN: DispatcherProfile: %s" case utils.MetaDispatcherHosts: - dpPrf := &engine.DispatcherHost{ - Tenant: tntID.Tenant, - RemoteHost: &config.RemoteHost{ - ID: tntID.ID, - Transport: utils.MetaJSON, - }, - } - if err = prepareData(dpPrf, lDataSet, rsrSep); err != nil { - return - } - utils.Logger.Info( - fmt.Sprintf("<%s-%s> DRY_RUN: DispatcherHost: %s", - utils.LoaderS, ldrID, utils.ToJSON(dpPrf))) + msg = "<%s-%s> DRY_RUN: DispatcherHost: %s" case utils.MetaRateProfiles: - rpl := &utils.RateProfile{ - Tenant: tntID.Tenant, - ID: tntID.ID, - Rates: make(map[string]*utils.Rate), - MinCost: utils.NewDecimal(0, 0), - MaxCost: utils.NewDecimal(0, 0), - } - if err = prepareData(rpl, lDataSet, rsrSep); err != nil { - return - } - utils.Logger.Info( - fmt.Sprintf("<%s-%s> DRY_RUN: RateProfile: %s", - utils.LoaderS, ldrID, utils.ToJSON(rpl))) - + msg = "<%s-%s> DRY_RUN: RateProfile: %s" case utils.MetaActionProfiles: - acp := &engine.ActionProfile{ - Tenant: tntID.Tenant, - ID: tntID.ID, - Targets: make(map[string]utils.StringSet), - } - if err = prepareData(acp, lDataSet, rsrSep); err != nil { - return - } - utils.Logger.Info( - fmt.Sprintf("<%s-%s> DRY_RUN: ActionProfile: %s", - utils.LoaderS, ldrID, utils.ToJSON(acp))) + msg = "<%s-%s> DRY_RUN: ActionProfile: %s" case utils.MetaAccounts: - acp := &utils.Account{ - Tenant: tntID.Tenant, - ID: tntID.ID, - Opts: make(map[string]interface{}), - Balances: make(map[string]*utils.Balance), - } - if err = prepareData(acp, lDataSet, rsrSep); err != nil { - return - } - utils.Logger.Info( - fmt.Sprintf("<%s-%s> DRY_RUN: Accounts: %s", - utils.LoaderS, ldrID, utils.ToJSON(acp))) + msg = "<%s-%s> DRY_RUN: Accounts: %s" } + utils.Logger.Info(fmt.Sprintf(msg, + utils.LoaderS, ldrID, utils.ToJSON(obj))) return } @@ -385,16 +181,16 @@ type loader struct { Locker } -func (l *loader) process(ctx *context.Context, tntID *utils.TenantID, lDataSet []*utils.OrderedNavigableMap, lType, action, caching string, withIndex, partialRates bool) (err error) { +func (l *loader) process(ctx *context.Context, obj profile, lType, action, caching string, withIndex, partialRates bool) (err error) { switch action { case utils.MetaParse: return case utils.MetaDryRun: - return dryRun(ctx, lType, l.cfg.GeneralCfg().RSRSep, l.ldrCfg.ID, tntID, lDataSet) + return dryRun(ctx, lType, l.ldrCfg.ID, obj) case utils.MetaStore: - err = setToDB(ctx, l.dm, lType, l.cfg.GeneralCfg().RSRSep, tntID, lDataSet, withIndex, partialRates) + err = setToDB(ctx, l.dm, lType, obj, withIndex, partialRates) case utils.MetaRemove: - err = removeFromDB(ctx, l.dm, lType, tntID.Tenant, tntID.ID, withIndex, partialRates, lDataSet[0]) + err = removeFromDB(ctx, l.dm, lType, withIndex, partialRates, obj) default: return fmt.Errorf("unsupported loader action: <%q>", action) } @@ -403,7 +199,7 @@ func (l *loader) process(ctx *context.Context, tntID *utils.TenantID, lDataSet [ } cacheArgs := make(map[string][]string) var cacheIDs []string // verify if we need to clear indexe - tntId := tntID.TenantID() + tntId := obj.TenantID() switch lType { case utils.MetaAttributes: cacheIDs = []string{utils.CacheAttributeFilterIndexes} @@ -448,8 +244,9 @@ func (l *loader) process(ctx *context.Context, tntID *utils.TenantID, lDataSet [ } func (l *loader) processData(ctx *context.Context, csv CSVReader, tmpls []*config.FCTemplate, lType, action, caching string, withIndex, partialRates bool) (err error) { - var prevTntID *utils.TenantID - var lData []*utils.OrderedNavigableMap + newPrf := newProfileFunc(lType) + obj := newPrf() + var prevTntID string for lineNr := 1; ; lineNr++ { var record []string if record, err = csv.Read(); err != nil { @@ -462,27 +259,29 @@ func (l *loader) processData(ctx *context.Context, csv CSVReader, tmpls []*confi utils.LoaderS, l.ldrCfg.ID, csv.Path(), lineNr, err)) return } - var data *utils.OrderedNavigableMap - if data, err = newRecord(ctx, config.NewSliceDP(record, nil), tmpls, l.ldrCfg.Tenant, l.filterS, l.cfg, l.dataCache[lType]); err != nil { + + tmp := newPrf() + if err = newRecord(config.NewSliceDP(record, nil), tmp, l.ldrCfg.Tenant, l.cfg, l.dataCache[lType]). + SetFields(ctx, tmpls, l.filterS, l.cfg.GeneralCfg().RoundingDecimals, l.cfg.GeneralCfg().DefaultTimezone, l.cfg.GeneralCfg().RSRSep); err != nil { utils.Logger.Warning( fmt.Sprintf("<%s> <%s> file<%s> line: %d, error: %s", utils.LoaderS, l.ldrCfg.ID, csv.Path(), lineNr, err)) return } - tntID := TenantIDFromOrderedNavigableMap(data) - if !prevTntID.Equal(tntID) { - if prevTntID != nil { - if err = l.process(ctx, prevTntID, lData, lType, action, caching, withIndex, partialRates); err != nil { + + if tntID := tmp.TenantID(); prevTntID != tntID { + if len(prevTntID) != 0 { + if err = l.process(ctx, obj, lType, action, caching, withIndex, partialRates); err != nil { return } } prevTntID = tntID - lData = make([]*utils.OrderedNavigableMap, 0, 1) + obj = newPrf() } - lData = append(lData, data) + obj.Merge(tmp) } - if prevTntID != nil { - err = l.process(ctx, prevTntID, lData, lType, action, caching, withIndex, partialRates) + if len(prevTntID) != 0 { + err = l.process(ctx, obj, lType, action, caching, withIndex, partialRates) } return } diff --git a/loaders/loader_test.go b/loaders/loader_test.go index e63309e20..aa4c062f6 100644 --- a/loaders/loader_test.go +++ b/loaders/loader_test.go @@ -46,23 +46,23 @@ func TestRemoveFromDB(t *testing.T) { for _, lType := range []string{utils.MetaAttributes, utils.MetaResources, utils.MetaFilters, utils.MetaStats, utils.MetaThresholds, utils.MetaRoutes, utils.MetaChargers, utils.MetaDispatchers, utils.MetaDispatcherHosts, utils.MetaRateProfiles, utils.MetaActionProfiles, utils.MetaAccounts} { - if err := removeFromDB(context.Background(), dm, lType, "cgrates.org", "ID", true, false, utils.NewOrderedNavigableMap()); err != utils.ErrNotFound { + if err := removeFromDB(context.Background(), dm, lType, true, false, profileTest{utils.Tenant: "cgrates.org", utils.ID: "ID"}); err != utils.ErrNotFound { t.Error(err) } } expErrMsg := "cannot find RateIDs in map" - if err := removeFromDB(context.Background(), dm, utils.MetaRateProfiles, "cgrates.org", "ID", true, true, utils.NewOrderedNavigableMap()); err == nil || err.Error() != expErrMsg { + if err := removeFromDB(context.Background(), dm, utils.MetaRateProfiles, true, true, profileTest{utils.Tenant: "cgrates.org", utils.ID: "ID"}); err == nil || err.Error() != expErrMsg { t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) } - if err := removeFromDB(context.Background(), dm, utils.MetaRateProfiles, "cgrates.org", "ID", true, true, newOrderNavMap(utils.MapStorage{utils.RateIDs: "RT1"})); err != utils.ErrNotFound { + if err := removeFromDB(context.Background(), dm, utils.MetaRateProfiles, true, true, &utils.RateProfile{Tenant: "cgrates.org", ID: "ID", Rates: map[string]*utils.Rate{"RT1": {}}}); err != utils.ErrNotFound { t.Error(err) } - if err := removeFromDB(context.Background(), dm, utils.EmptyString, "cgrates.org", "ID", true, false, utils.NewOrderedNavigableMap()); err != nil { + if err := removeFromDB(context.Background(), dm, utils.EmptyString, true, false, profileTest{utils.Tenant: "cgrates.org", utils.ID: "ID"}); err != nil { t.Error(err) } } -func testDryRunWithData(lType string, data []utils.MapStorage) (string, error) { +func testDryRunWithData(lType string, data profile) (string, error) { tmpLogger := utils.Logger defer func() { utils.Logger = tmpLogger @@ -70,15 +70,15 @@ func testDryRunWithData(lType string, data []utils.MapStorage) (string, error) { var buf bytes.Buffer utils.Logger = utils.NewStdLoggerWithWriter(&buf, "", 7) - err := dryRun(context.Background(), lType, utils.InfieldSep, "test", TenantIDFromOrderedNavigableMap(data[0]), data) + err := dryRun(context.Background(), lType, "test", data) return buf.String(), err } func testDryRun(t *testing.T, lType string) string { - data := utils.NewOrderedNavigableMap() - data.SetAsSlice(utils.NewFullPath(utils.Tenant), []*utils.DataNode{utils.NewLeafNode("cgrates.org")}) - data.SetAsSlice(utils.NewFullPath(utils.ID), []*utils.DataNode{utils.NewLeafNode("ID")}) - buf, err := testDryRunWithData(lType, []*utils.OrderedNavigableMap{data}) + buf, err := testDryRunWithData(lType, profileTest{ + utils.Tenant: "cgrates.org", + utils.ID: "ID", + }) if err != nil { t.Fatal(lType, err) } @@ -93,241 +93,96 @@ func newOrderNavMap(mp utils.MapStorage) (o *utils.OrderedNavigableMap) { return } func TestDryRun(t *testing.T) { - if expLog, rplyLog := "[INFO] DRY_RUN: AttributeProfile: {\"Tenant\":\"cgrates.org\",\"ID\":\"ID\",\"FilterIDs\":null,\"Attributes\":null,\"Blocker\":false,\"Weight\":0}", + if expLog, rplyLog := "[INFO] DRY_RUN: AttributeProfile: {\"Tenant\":\"cgrates.org\",\"ID\":\"ID\"}", testDryRun(t, utils.MetaAttributes); !strings.Contains(rplyLog, expLog) { t.Errorf("Expected %+q, received %+q", expLog, rplyLog) } - if expLog, rplyLog := "[INFO] DRY_RUN: ResourceProfile: {\"Tenant\":\"cgrates.org\",\"ID\":\"ID\",\"FilterIDs\":null,\"UsageTTL\":0,\"Limit\":0,\"AllocationMessage\":\"\",\"Blocker\":false,\"Stored\":false,\"Weight\":0,\"ThresholdIDs\":null}", + if expLog, rplyLog := "[INFO] DRY_RUN: ResourceProfile: {\"Tenant\":\"cgrates.org\",\"ID\":\"ID\"}", testDryRun(t, utils.MetaResources); !strings.Contains(rplyLog, expLog) { t.Errorf("Expected %+q, received %+q", expLog, rplyLog) } - if expLog, rplyLog := "[INFO] DRY_RUN: Filter: {\"Tenant\":\"cgrates.org\",\"ID\":\"ID\",\"Rules\":[]}", + if expLog, rplyLog := "[INFO] DRY_RUN: Filter: {\"Tenant\":\"cgrates.org\",\"ID\":\"ID\"}", testDryRun(t, utils.MetaFilters); !strings.Contains(rplyLog, expLog) { t.Errorf("Expected %+q, received %+q", expLog, rplyLog) } - if expLog, rplyLog := "[INFO] DRY_RUN: StatsQueueProfile: {\"Tenant\":\"cgrates.org\",\"ID\":\"ID\",\"FilterIDs\":null,\"QueueLength\":0,\"TTL\":0,\"MinItems\":0,\"Metrics\":null,\"Stored\":false,\"Blocker\":false,\"Weight\":0,\"ThresholdIDs\":null}", + if expLog, rplyLog := "[INFO] DRY_RUN: StatsQueueProfile: {\"Tenant\":\"cgrates.org\",\"ID\":\"ID\"}", testDryRun(t, utils.MetaStats); !strings.Contains(rplyLog, expLog) { t.Errorf("Expected %+q, received %+q", expLog, rplyLog) } - if expLog, rplyLog := "[INFO] DRY_RUN: ThresholdProfile: {\"Tenant\":\"cgrates.org\",\"ID\":\"ID\",\"FilterIDs\":null,\"MaxHits\":0,\"MinHits\":0,\"MinSleep\":0,\"Blocker\":false,\"Weight\":0,\"ActionProfileIDs\":null,\"Async\":false}", + if expLog, rplyLog := "[INFO] DRY_RUN: ThresholdProfile: {\"Tenant\":\"cgrates.org\",\"ID\":\"ID\"}", testDryRun(t, utils.MetaThresholds); !strings.Contains(rplyLog, expLog) { t.Errorf("Expected %+q, received %+q", expLog, rplyLog) } - if expLog, rplyLog := "[INFO] DRY_RUN: RouteProfile: {\"Tenant\":\"cgrates.org\",\"ID\":\"ID\",\"FilterIDs\":null,\"Sorting\":\"\",\"SortingParameters\":null,\"Routes\":null,\"Weights\":null}", + if expLog, rplyLog := "[INFO] DRY_RUN: RouteProfile: {\"Tenant\":\"cgrates.org\",\"ID\":\"ID\"}", testDryRun(t, utils.MetaRoutes); !strings.Contains(rplyLog, expLog) { t.Errorf("Expected %+q, received %+q", expLog, rplyLog) } - if expLog, rplyLog := "[INFO] DRY_RUN: ChargerProfile: {\"Tenant\":\"cgrates.org\",\"ID\":\"ID\",\"FilterIDs\":null,\"RunID\":\"\",\"AttributeIDs\":null,\"Weight\":0}", + if expLog, rplyLog := "[INFO] DRY_RUN: ChargerProfile: {\"Tenant\":\"cgrates.org\",\"ID\":\"ID\"}", testDryRun(t, utils.MetaChargers); !strings.Contains(rplyLog, expLog) { t.Errorf("Expected %+q, received %+q", expLog, rplyLog) } - if expLog, rplyLog := "[INFO] DRY_RUN: DispatcherProfile: {\"Tenant\":\"cgrates.org\",\"ID\":\"ID\",\"FilterIDs\":null,\"Strategy\":\"\",\"StrategyParams\":{},\"Weight\":0,\"Hosts\":null}", + if expLog, rplyLog := "[INFO] DRY_RUN: DispatcherProfile: {\"Tenant\":\"cgrates.org\",\"ID\":\"ID\"}", testDryRun(t, utils.MetaDispatchers); !strings.Contains(rplyLog, expLog) { t.Errorf("Expected %+q, received %+q", expLog, rplyLog) } - if expLog, rplyLog := "[INFO] DRY_RUN: RateProfile: {\"Tenant\":\"cgrates.org\",\"ID\":\"ID\",\"FilterIDs\":null,\"Weights\":null,\"MinCost\":0,\"MaxCost\":0,\"MaxCostStrategy\":\"\",\"Rates\":{}}", + if expLog, rplyLog := "[INFO] DRY_RUN: RateProfile: {\"Tenant\":\"cgrates.org\",\"ID\":\"ID\"}", testDryRun(t, utils.MetaRateProfiles); !strings.Contains(rplyLog, expLog) { t.Errorf("Expected %+q, received %+q", expLog, rplyLog) } - if expLog, rplyLog := "[INFO] DRY_RUN: ActionProfile: {\"Tenant\":\"cgrates.org\",\"ID\":\"ID\",\"FilterIDs\":null,\"Weight\":0,\"Schedule\":\"\",\"Targets\":{},\"Actions\":null}", + if expLog, rplyLog := "[INFO] DRY_RUN: ActionProfile: {\"Tenant\":\"cgrates.org\",\"ID\":\"ID\"}", testDryRun(t, utils.MetaActionProfiles); !strings.Contains(rplyLog, expLog) { t.Errorf("Expected %+q, received %+q", expLog, rplyLog) } - if expLog, rplyLog := "[INFO] DRY_RUN: Accounts: {\"Tenant\":\"cgrates.org\",\"ID\":\"ID\",\"FilterIDs\":null,\"Weights\":null,\"Opts\":{},\"Balances\":{},\"ThresholdIDs\":null}", + if expLog, rplyLog := "[INFO] DRY_RUN: Accounts: {\"Tenant\":\"cgrates.org\",\"ID\":\"ID\"}", testDryRun(t, utils.MetaAccounts); !strings.Contains(rplyLog, expLog) { t.Errorf("Expected %+q, received %+q", expLog, rplyLog) } - - rplyLog, err := testDryRunWithData(utils.MetaDispatcherHosts, []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID", utils.Address: "127.0.0.1"})}) - if err != nil { - t.Fatal(err) - } - if expLog := "[INFO] DRY_RUN: DispatcherHost: {\"Tenant\":\"cgrates.org\",\"ID\":\"ID\",\"Address\":\"127.0.0.1\",\"Transport\":\"*json\",\"ConnectAttempts\":0,\"Reconnects\":0,\"ConnectTimeout\":0,\"ReplyTimeout\":0,\"TLS\":false,\"ClientKey\":\"\",\"ClientCertificate\":\"\",\"CaCertificate\":\"\"}"; !strings.Contains(rplyLog, expLog) { + if expLog, rplyLog := "[INFO] DRY_RUN: DispatcherHost: {\"Tenant\":\"cgrates.org\",\"ID\":\"ID\"}", + testDryRun(t, utils.MetaDispatcherHosts); !strings.Contains(rplyLog, expLog) { t.Errorf("Expected %+q, received %+q", expLog, rplyLog) } } -func TestDryRunWithUpdateStructErrors(t *testing.T) { - expErrMsg := `strconv.ParseFloat: parsing "notWeight": invalid syntax` - if _, err := testDryRunWithData(utils.MetaAttributes, []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Weight: "notWeight"})}); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - - if _, err := testDryRunWithData(utils.MetaResources, []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Weight: "notWeight"})}); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - if _, err := testDryRunWithData(utils.MetaStats, []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Weight: "notWeight"})}); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - if _, err := testDryRunWithData(utils.MetaThresholds, []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Weight: "notWeight"})}); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - - if _, err := testDryRunWithData(utils.MetaChargers, []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Weight: "notWeight"})}); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - if _, err := testDryRunWithData(utils.MetaDispatchers, []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Weight: "notWeight"})}); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - - if _, err := testDryRunWithData(utils.MetaActionProfiles, []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Weight: "notWeight"})}); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } -} - -func TestDryRunWithModelsErrors(t *testing.T) { - expErrMsg := `strconv.ParseFloat: parsing "float": invalid syntax` - if _, err := testDryRunWithData(utils.MetaResources, []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID", "Limit": "float"})}); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - - expErrMsg = `time: invalid duration "float"` - if _, err := testDryRunWithData(utils.MetaStats, []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID", "TTL": "float"})}); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - if _, err := testDryRunWithData(utils.MetaThresholds, []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID", "MinSleep": "float"})}); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - expErrMsg = `invalid Weight in string: <;float>` - if _, err := testDryRunWithData(utils.MetaRoutes, []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID", "Weights": ";float"})}); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - if _, err := testDryRunWithData(utils.MetaRateProfiles, []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID", "Weights": ";float"})}); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - if _, err := testDryRunWithData(utils.MetaAccounts, []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID", "Weights": ";float"})}); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - - expErrMsg = `time: invalid duration "float"` - if _, err := testDryRunWithData(utils.MetaDispatcherHosts, []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID", "ReplyTimeout": "float", "Address": "127.0.0.1"})}); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - - if _, err := testDryRunWithData(utils.MetaFilters, []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID", "ReplyTimeout": "float"})}); err != utils.ErrWrongPath { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - - expErrMsg = `emtpy RSRParser in rule: <>` - data := newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID"}) - data.SetAsSlice(utils.NewFullPath("Rules.Type"), []*utils.DataNode{utils.NewLeafNode("*no")}) - if _, err := testDryRunWithData(utils.MetaFilters, []*utils.OrderedNavigableMap{data}); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } -} - -func TestSetToDBWithUpdateStructErrors(t *testing.T) { - expErrMsg := `strconv.ParseFloat: parsing "notWeight": invalid syntax` - if err := setToDB(context.Background(), nil, utils.MetaAttributes, utils.InfieldSep, utils.NewTenantID(""), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Weight: "notWeight"})}, true, false); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - - if err := setToDB(context.Background(), nil, utils.MetaResources, utils.InfieldSep, utils.NewTenantID(""), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Weight: "notWeight"})}, true, false); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - if err := setToDB(context.Background(), nil, utils.MetaStats, utils.InfieldSep, utils.NewTenantID(""), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Weight: "notWeight"})}, true, false); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - if err := setToDB(context.Background(), nil, utils.MetaThresholds, utils.InfieldSep, utils.NewTenantID(""), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Weight: "notWeight"})}, true, false); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - - if err := setToDB(context.Background(), nil, utils.MetaChargers, utils.InfieldSep, utils.NewTenantID(""), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Weight: "notWeight"})}, true, false); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - if err := setToDB(context.Background(), nil, utils.MetaDispatchers, utils.InfieldSep, utils.NewTenantID(""), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Weight: "notWeight"})}, true, false); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - - if err := setToDB(context.Background(), nil, utils.MetaActionProfiles, utils.InfieldSep, utils.NewTenantID(""), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Weight: "notWeight"})}, true, false); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - -} - -func TestSetToDBWithModelsErrors(t *testing.T) { - expErrMsg := `strconv.ParseFloat: parsing "float": invalid syntax` - if err := setToDB(context.Background(), nil, utils.MetaResources, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID", "Limit": "float"})}, true, false); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - expErrMsg = `time: invalid duration "float"` - if err := setToDB(context.Background(), nil, utils.MetaStats, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID", "TTL": "float"})}, true, false); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - if err := setToDB(context.Background(), nil, utils.MetaThresholds, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID", "MinSleep": "float"})}, true, false); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - expErrMsg = `invalid Weight in string: <;float>` - if err := setToDB(context.Background(), nil, utils.MetaRoutes, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID", "Weights": ";float"})}, true, false); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - if err := setToDB(context.Background(), nil, utils.MetaRateProfiles, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID", "Weights": ";float"})}, true, false); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - if err := setToDB(context.Background(), nil, utils.MetaAccounts, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID", "Weights": ";float"})}, true, false); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - - expErrMsg = `time: invalid duration "float"` - if err := setToDB(context.Background(), nil, utils.MetaDispatcherHosts, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID", "ReplyTimeout": "float", "Address": "127.0.0.1"})}, true, false); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - - if err := setToDB(context.Background(), nil, utils.MetaFilters, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID", "ReplyTimeout": "float"})}, true, false); err != utils.ErrWrongPath { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - - expErrMsg = `emtpy RSRParser in rule: <>` - data := newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID"}) - data.SetAsSlice(utils.NewFullPath("Rules.Type"), []*utils.DataNode{utils.NewLeafNode("*no")}) - if err := setToDB(context.Background(), nil, utils.MetaFilters, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{data}, true, false); err == nil || err.Error() != expErrMsg { - t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) - } - if err := setToDB(context.Background(), nil, utils.EmptyString, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID"})}, true, false); err != nil { - t.Error(err) - } -} - func TestSetToDBWithDBError(t *testing.T) { - if err := setToDB(context.Background(), nil, utils.MetaAttributes, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID"})}, true, false); err != utils.ErrNoDatabaseConn { + if err := setToDB(context.Background(), nil, utils.MetaAttributes, newProfileFunc(utils.MetaAttributes)(), true, false); err != utils.ErrNoDatabaseConn { t.Fatal(err) } - if err := setToDB(context.Background(), nil, utils.MetaResources, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID"})}, true, false); err != utils.ErrNoDatabaseConn { + if err := setToDB(context.Background(), nil, utils.MetaResources, newProfileFunc(utils.MetaResources)(), true, false); err != utils.ErrNoDatabaseConn { t.Fatal(err) } - if err := setToDB(context.Background(), nil, utils.MetaStats, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID"})}, true, false); err != utils.ErrNoDatabaseConn { + if err := setToDB(context.Background(), nil, utils.MetaStats, newProfileFunc(utils.MetaStats)(), true, false); err != utils.ErrNoDatabaseConn { t.Fatal(err) } - if err := setToDB(context.Background(), nil, utils.MetaThresholds, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID"})}, true, false); err != utils.ErrNoDatabaseConn { + if err := setToDB(context.Background(), nil, utils.MetaThresholds, newProfileFunc(utils.MetaThresholds)(), true, false); err != utils.ErrNoDatabaseConn { t.Fatal(err) } - if err := setToDB(context.Background(), nil, utils.MetaChargers, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID"})}, true, false); err != utils.ErrNoDatabaseConn { + if err := setToDB(context.Background(), nil, utils.MetaChargers, newProfileFunc(utils.MetaChargers)(), true, false); err != utils.ErrNoDatabaseConn { t.Fatal(err) } - if err := setToDB(context.Background(), nil, utils.MetaDispatchers, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID"})}, true, false); err != utils.ErrNoDatabaseConn { + if err := setToDB(context.Background(), nil, utils.MetaDispatchers, newProfileFunc(utils.MetaDispatchers)(), true, false); err != utils.ErrNoDatabaseConn { t.Fatal(err) } - if err := setToDB(context.Background(), nil, utils.MetaActionProfiles, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID"})}, true, false); err != utils.ErrNoDatabaseConn { + if err := setToDB(context.Background(), nil, utils.MetaActionProfiles, newProfileFunc(utils.MetaActionProfiles)(), true, false); err != utils.ErrNoDatabaseConn { t.Fatal(err) } - if err := setToDB(context.Background(), nil, utils.MetaFilters, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID"})}, true, false); err != utils.ErrNoDatabaseConn { + if err := setToDB(context.Background(), nil, utils.MetaFilters, newProfileFunc(utils.MetaFilters)(), true, false); err != utils.ErrNoDatabaseConn { t.Fatal(err) } - if err := setToDB(context.Background(), nil, utils.MetaRoutes, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID"})}, true, false); err != utils.ErrNoDatabaseConn { + if err := setToDB(context.Background(), nil, utils.MetaRoutes, newProfileFunc(utils.MetaRoutes)(), true, false); err != utils.ErrNoDatabaseConn { t.Fatal(err) } - if err := setToDB(context.Background(), nil, utils.MetaDispatcherHosts, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID", "Address": "127.0.0.1"})}, true, false); err != utils.ErrNoDatabaseConn { + if err := setToDB(context.Background(), nil, utils.MetaDispatcherHosts, newProfileFunc(utils.MetaDispatcherHosts)(), true, false); err != utils.ErrNoDatabaseConn { t.Fatal(err) } - if err := setToDB(context.Background(), nil, utils.MetaRateProfiles, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID"})}, true, false); err != utils.ErrNoDatabaseConn { + if err := setToDB(context.Background(), nil, utils.MetaRateProfiles, newProfileFunc(utils.MetaRateProfiles)(), true, false); err != utils.ErrNoDatabaseConn { t.Fatal(err) } - if err := setToDB(context.Background(), nil, utils.MetaAccounts, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID"})}, true, false); err != utils.ErrNoDatabaseConn { + if err := setToDB(context.Background(), nil, utils.MetaAccounts, newProfileFunc(utils.MetaAccounts)(), true, false); err != utils.ErrNoDatabaseConn { t.Fatal(err) } } @@ -335,131 +190,131 @@ func TestSetToDBWithDBError(t *testing.T) { func TestSetToDB(t *testing.T) { cfg := config.NewDefaultCGRConfig() dm := engine.NewDataManager(engine.NewInternalDB(nil, nil, cfg.DataDbCfg().Items), cfg.CacheCfg(), nil) - if err := setToDB(context.Background(), dm, utils.MetaAttributes, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID"})}, true, false); err != nil { + v1 := &engine.AttributeProfile{Tenant: "cgrates.org", ID: "ID"} + if err := setToDB(context.Background(), dm, utils.MetaAttributes, v1, true, false); err != nil { t.Fatal(err) } - v1 := &engine.AttributeProfile{Tenant: "cgrates.org", ID: "ID"} if prf, err := dm.GetAttributeProfile(context.Background(), "cgrates.org", "ID", true, true, utils.NonTransactional); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(v1, prf) { t.Errorf("Expeceted: %v, received: %v", utils.ToJSON(v1), utils.ToJSON(prf)) } - if err := setToDB(context.Background(), dm, utils.MetaResources, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID"})}, true, false); err != nil { + v2 := &engine.ResourceProfile{Tenant: "cgrates.org", ID: "ID"} + if err := setToDB(context.Background(), dm, utils.MetaResources, v2, true, false); err != nil { t.Fatal(err) } - v2 := &engine.ResourceProfile{Tenant: "cgrates.org", ID: "ID"} if prf, err := dm.GetResourceProfile(context.Background(), "cgrates.org", "ID", true, true, utils.NonTransactional); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(v2, prf) { t.Errorf("Expeceted: %v, received: %v", utils.ToJSON(v2), utils.ToJSON(prf)) } - if err := setToDB(context.Background(), dm, utils.MetaStats, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID"})}, true, false); err != nil { + v3 := &engine.StatQueueProfile{Tenant: "cgrates.org", ID: "ID"} + if err := setToDB(context.Background(), dm, utils.MetaStats, v3, true, false); err != nil { t.Fatal(err) } - v3 := &engine.StatQueueProfile{Tenant: "cgrates.org", ID: "ID"} if prf, err := dm.GetStatQueueProfile(context.Background(), "cgrates.org", "ID", true, true, utils.NonTransactional); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(v3, prf) { t.Errorf("Expeceted: %v, received: %v", utils.ToJSON(v3), utils.ToJSON(prf)) } - if err := setToDB(context.Background(), dm, utils.MetaThresholds, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID"})}, true, false); err != nil { + v4 := &engine.ThresholdProfile{Tenant: "cgrates.org", ID: "ID"} + if err := setToDB(context.Background(), dm, utils.MetaThresholds, v4, true, false); err != nil { t.Fatal(err) } - v4 := &engine.ThresholdProfile{Tenant: "cgrates.org", ID: "ID"} if prf, err := dm.GetThresholdProfile(context.Background(), "cgrates.org", "ID", true, true, utils.NonTransactional); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(v4, prf) { t.Errorf("Expeceted: %v, received: %v", utils.ToJSON(v4), utils.ToJSON(prf)) } - if err := setToDB(context.Background(), dm, utils.MetaChargers, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID"})}, true, false); err != nil { + v5 := &engine.ChargerProfile{Tenant: "cgrates.org", ID: "ID"} + if err := setToDB(context.Background(), dm, utils.MetaChargers, v5, true, false); err != nil { t.Fatal(err) } - v5 := &engine.ChargerProfile{Tenant: "cgrates.org", ID: "ID"} if prf, err := dm.GetChargerProfile(context.Background(), "cgrates.org", "ID", true, true, utils.NonTransactional); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(v5, prf) { t.Errorf("Expeceted: %v, received: %v", utils.ToJSON(v5), utils.ToJSON(prf)) } - if err := setToDB(context.Background(), dm, utils.MetaDispatchers, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID"})}, true, false); err != nil { + v6 := &engine.DispatcherProfile{Tenant: "cgrates.org", ID: "ID", StrategyParams: make(map[string]interface{})} + if err := setToDB(context.Background(), dm, utils.MetaDispatchers, v6, true, false); err != nil { t.Fatal(err) } - v6 := &engine.DispatcherProfile{Tenant: "cgrates.org", ID: "ID", StrategyParams: make(map[string]interface{})} if prf, err := dm.GetDispatcherProfile(context.Background(), "cgrates.org", "ID", true, true, utils.NonTransactional); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(v6, prf) { t.Errorf("Expeceted: %v, received: %v", utils.ToJSON(v6), utils.ToJSON(prf)) } - if err := setToDB(context.Background(), dm, utils.MetaActionProfiles, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID"})}, true, false); err != nil { + v7 := &engine.ActionProfile{Tenant: "cgrates.org", ID: "ID", Targets: map[string]utils.StringSet{}} + if err := setToDB(context.Background(), dm, utils.MetaActionProfiles, v7, true, false); err != nil { t.Fatal(err) } - v7 := &engine.ActionProfile{Tenant: "cgrates.org", ID: "ID", Targets: map[string]utils.StringSet{}} if prf, err := dm.GetActionProfile(context.Background(), "cgrates.org", "ID", true, true, utils.NonTransactional); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(v7, prf) { t.Errorf("Expeceted: %v, received: %v", utils.ToJSON(v7), utils.ToJSON(prf)) } - if err := setToDB(context.Background(), dm, utils.MetaFilters, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID"})}, true, false); err != nil { - t.Fatal(err) - } v8 := &engine.Filter{Tenant: "cgrates.org", ID: "ID", Rules: make([]*engine.FilterRule, 0)} v8.Compile() + if err := setToDB(context.Background(), dm, utils.MetaFilters, v8, true, false); err != nil { + t.Fatal(err) + } if prf, err := dm.GetFilter(context.Background(), "cgrates.org", "ID", true, true, utils.NonTransactional); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(v8, prf) { t.Errorf("Expeceted: %v, received: %v", utils.ToJSON(v8), utils.ToJSON(prf)) } - if err := setToDB(context.Background(), dm, utils.MetaRoutes, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID"})}, true, false); err != nil { + v9 := &engine.RouteProfile{Tenant: "cgrates.org", ID: "ID"} + if err := setToDB(context.Background(), dm, utils.MetaRoutes, v9, true, false); err != nil { t.Fatal(err) } - v9 := &engine.RouteProfile{Tenant: "cgrates.org", ID: "ID"} if prf, err := dm.GetRouteProfile(context.Background(), "cgrates.org", "ID", true, true, utils.NonTransactional); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(v9, prf) { t.Errorf("Expeceted: %v, received: %v", utils.ToJSON(v9), utils.ToJSON(prf)) } - if err := setToDB(context.Background(), dm, utils.MetaDispatcherHosts, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID", "Address": "127.0.0.1"})}, true, false); err != nil { + v10 := &engine.DispatcherHost{Tenant: "cgrates.org", RemoteHost: &config.RemoteHost{ID: "ID", Address: "127.0.0.1", Transport: utils.MetaJSON}} + if err := setToDB(context.Background(), dm, utils.MetaDispatcherHosts, v10, true, false); err != nil { t.Fatal(err) } - v10 := &engine.DispatcherHost{Tenant: "cgrates.org", RemoteHost: &config.RemoteHost{ID: "ID", Address: "127.0.0.1", Transport: utils.MetaJSON}} if prf, err := dm.GetDispatcherHost(context.Background(), "cgrates.org", "ID", true, true, utils.NonTransactional); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(v10, prf) { t.Errorf("Expeceted: %v, received: %v", utils.ToJSON(v10), utils.ToJSON(prf)) } - if err := setToDB(context.Background(), dm, utils.MetaRateProfiles, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID"})}, true, false); err != nil { + v11 := &utils.RateProfile{Tenant: "cgrates.org", ID: "ID", Rates: map[string]*utils.Rate{}, MinCost: utils.NewDecimal(0, 0), MaxCost: utils.NewDecimal(0, 0)} + if err := setToDB(context.Background(), dm, utils.MetaRateProfiles, v11, true, false); err != nil { t.Fatal(err) } - v11 := &utils.RateProfile{Tenant: "cgrates.org", ID: "ID", Rates: map[string]*utils.Rate{}, MinCost: utils.NewDecimal(0, 0), MaxCost: utils.NewDecimal(0, 0)} if prf, err := dm.GetRateProfile(context.Background(), "cgrates.org", "ID", true, true, utils.NonTransactional); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(v11, prf) { t.Errorf("Expeceted: %v, received: %v", utils.ToJSON(v11), utils.ToJSON(prf)) } - if err := setToDB(context.Background(), dm, utils.MetaAccounts, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID"})}, true, false); err != nil { + v12 := &utils.Account{Tenant: "cgrates.org", ID: "ID", Balances: map[string]*utils.Balance{}, Opts: make(map[string]interface{})} + if err := setToDB(context.Background(), dm, utils.MetaAccounts, v12, true, false); err != nil { t.Fatal(err) } - v12 := &utils.Account{Tenant: "cgrates.org", ID: "ID", Balances: map[string]*utils.Balance{}, Opts: make(map[string]interface{})} if prf, err := dm.GetAccount(context.Background(), "cgrates.org", "ID"); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(v12, prf) { t.Errorf("Expeceted: %v, received: %v", utils.ToJSON(v12), utils.ToJSON(prf)) } - if err := setToDB(context.Background(), dm, utils.MetaRateProfiles, utils.InfieldSep, utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID"})}, true, true); err != nil { + v13 := &utils.RateProfile{Tenant: "cgrates.org", ID: "ID", Rates: map[string]*utils.Rate{}, MinCost: utils.NewDecimal(0, 0), MaxCost: utils.NewDecimal(0, 0)} + if err := setToDB(context.Background(), dm, utils.MetaRateProfiles, v13, true, true); err != nil { t.Fatal(err) } - v13 := &utils.RateProfile{Tenant: "cgrates.org", ID: "ID", Rates: map[string]*utils.Rate{}, MinCost: utils.NewDecimal(0, 0), MaxCost: utils.NewDecimal(0, 0)} if prf, err := dm.GetRateProfile(context.Background(), "cgrates.org", "ID", true, true, utils.NonTransactional); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(v13, prf) { @@ -490,11 +345,11 @@ func TestLoaderProcess(t *testing.T) { } expErrMsg := `unsupported loader action: <"notSupported">` - if err := ld.process(context.Background(), utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{}, utils.MetaAttributes, "notSupported", utils.MetaNone, true, false); err == nil || err.Error() != expErrMsg { + if err := ld.process(context.Background(), nil, utils.MetaAttributes, "notSupported", utils.MetaNone, true, false); err == nil || err.Error() != expErrMsg { t.Errorf("Expeceted: %v, received: %v", expErrMsg, err) } - if err := ld.process(context.Background(), utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{}, utils.MetaAttributes, utils.MetaParse, utils.MetaNone, true, false); err != nil { + if err := ld.process(context.Background(), nil, utils.MetaAttributes, utils.MetaParse, utils.MetaNone, true, false); err != nil { t.Error(err) } @@ -505,25 +360,25 @@ func TestLoaderProcess(t *testing.T) { var buf bytes.Buffer utils.Logger = utils.NewStdLoggerWithWriter(&buf, "", 7) - if err := ld.process(context.Background(), utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID"})}, utils.MetaAttributes, utils.MetaDryRun, utils.MetaNone, true, false); err != nil { + if err := ld.process(context.Background(), profileTest{utils.Tenant: "cgrates.org", utils.ID: "ID"}, utils.MetaAttributes, utils.MetaDryRun, utils.MetaNone, true, false); err != nil { t.Error(err) } - if expLog, rplyLog := "[INFO] DRY_RUN: AttributeProfile: {\"Tenant\":\"cgrates.org\",\"ID\":\"ID\",\"FilterIDs\":null,\"Attributes\":null,\"Blocker\":false,\"Weight\":0}", + if expLog, rplyLog := "[INFO] DRY_RUN: AttributeProfile: {\"Tenant\":\"cgrates.org\",\"ID\":\"ID\"}", buf.String(); !strings.Contains(rplyLog, expLog) { t.Errorf("Expected %+q, received %+q", expLog, rplyLog) } - if err := ld.process(context.Background(), utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID"})}, utils.MetaAttributes, utils.MetaStore, utils.MetaNone, true, false); err != nil { + v1 := &engine.AttributeProfile{Tenant: "cgrates.org", ID: "ID"} + if err := ld.process(context.Background(), v1, utils.MetaAttributes, utils.MetaStore, utils.MetaNone, true, false); err != nil { t.Error(err) } - v1 := &engine.AttributeProfile{Tenant: "cgrates.org", ID: "ID"} if prf, err := dm.GetAttributeProfile(context.Background(), "cgrates.org", "ID", true, true, utils.NonTransactional); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(v1, prf) { t.Errorf("Expeceted: %v, received: %v", utils.ToJSON(v1), utils.ToJSON(prf)) } - if err := ld.process(context.Background(), utils.NewTenantID("cgrates.org:ID"), []*utils.OrderedNavigableMap{newOrderNavMap(utils.MapStorage{utils.Tenant: "cgrates.org", utils.ID: "ID"})}, utils.MetaAttributes, utils.MetaRemove, utils.MetaNone, true, false); err != nil { + if err := ld.process(context.Background(), v1, utils.MetaAttributes, utils.MetaRemove, utils.MetaNone, true, false); err != nil { t.Error(err) } if _, err := dm.GetAttributeProfile(context.Background(), "cgrates.org", "ID", false, true, utils.NonTransactional); err != utils.ErrNotFound { diff --git a/utils/account.go b/utils/account.go index 482c09c76..b96b96721 100644 --- a/utils/account.go +++ b/utils/account.go @@ -864,3 +864,22 @@ func (bL *Balance) Set(path []string, val interface{}, newBranch bool) (err erro } return ErrWrongPath } + +func (ap *Account) Merge(v2 interface{}) { + vi := v2.(*Account) + if len(vi.Tenant) != 0 { + ap.Tenant = vi.Tenant + } + if len(vi.ID) != 0 { + ap.ID = vi.ID + } + ap.FilterIDs = append(ap.FilterIDs, vi.FilterIDs...) + ap.Weights = append(ap.Weights, vi.Weights...) + ap.ThresholdIDs = append(ap.ThresholdIDs, vi.ThresholdIDs...) + for k, v := range vi.Opts { + ap.Opts[k] = v + } + for k, v := range vi.Balances { + ap.Balances[k] = v + } +} diff --git a/utils/dataconverter_test.go b/utils/dataconverter_test.go index 4af818b3d..61a324729 100644 --- a/utils/dataconverter_test.go +++ b/utils/dataconverter_test.go @@ -1354,7 +1354,7 @@ func TestJoinConverter(t *testing.T) { if err != nil { t.Fatal(err) } - expVal := "1,2,3,5" + expVal := "1;2;3;5" if i, err := d.Convert("1;2;3;5"); err != nil { t.Error(err.Error()) } else if expVal != i { @@ -1383,7 +1383,7 @@ func TestSplitConverter(t *testing.T) { t.Fatal(err) } expVal := []string{"1", "2", "3", "5"} - if i, err := d.Convert("1,2,3,5"); err != nil { + if i, err := d.Convert("1;2;3;5"); err != nil { t.Error(err.Error()) } else if !reflect.DeepEqual(expVal, i) { t.Errorf("expecting: %q, received: %q", expVal, i) diff --git a/utils/librates.go b/utils/librates.go index 6741e7085..cad4f8710 100644 --- a/utils/librates.go +++ b/utils/librates.go @@ -676,3 +676,28 @@ func (rt *Rate) Set(path []string, val interface{}, newBranch bool) (err error) } return } + +func (rp *RateProfile) Merge(v2 interface{}) { + vi := v2.(*RateProfile) + if len(vi.Tenant) != 0 { + rp.Tenant = vi.Tenant + } + if len(vi.ID) != 0 { + rp.ID = vi.ID + } + if len(vi.MaxCostStrategy) != 0 { + rp.MaxCostStrategy = vi.MaxCostStrategy + } + rp.FilterIDs = append(rp.FilterIDs, vi.FilterIDs...) + rp.Weights = append(rp.Weights, vi.Weights...) + for k, v := range vi.Rates { + rp.Rates[k] = v + } + o := decimal.New(0, 0) + if vi.MinCost != nil && vi.MinCost.Cmp(o) != 0 { + rp.MinCost = vi.MinCost + } + if vi.MaxCost != nil && vi.MinCost.Cmp(o) != 0 { + rp.MaxCost = vi.MaxCost + } +}