From 7a2817929fd902c736ac77e0cd683449bc1d370a Mon Sep 17 00:00:00 2001 From: TeoV Date: Fri, 26 Jun 2020 17:48:44 +0300 Subject: [PATCH] Add support for *virt export type in EEs --- config/config.go | 2 +- config/eescfg.go | 3 + data/conf/samples/ees/cgrates.json | 40 ++++++ ees/ee.go | 2 + ees/eereq.go | 4 +- ees/lib_test.go | 2 +- ees/virtualEe.go | 118 ++++++++++++++++++ ees/virtual_ee_it_test.go | 192 +++++++++++++++++++++++++++++ engine/filters.go | 2 +- utils/consts.go | 1 + utils/dynamicdataprovider.go | 60 +-------- utils/dynamicdataprovider_test.go | 66 +++------- 12 files changed, 379 insertions(+), 113 deletions(-) create mode 100644 ees/virtualEe.go create mode 100644 ees/virtual_ee_it_test.go diff --git a/config/config.go b/config/config.go index 8a77c8252..5f4554762 100755 --- a/config/config.go +++ b/config/config.go @@ -320,7 +320,7 @@ var possibleReaderTypes = utils.NewStringSet([]string{utils.MetaFileCSV, var possibleExporterTypes = utils.NewStringSet([]string{utils.MetaFileCSV, utils.META_NONE, utils.MetaFileFWV, utils.MetaHTTPPost, utils.MetaHTTPjsonMap, utils.MetaAMQPjsonMap, utils.MetaAMQPV1jsonMap, utils.MetaSQSjsonMap, - utils.MetaKafkajsonMap, utils.MetaS3jsonMap}) + utils.MetaKafkajsonMap, utils.MetaS3jsonMap, utils.MetaVirt}) func (cfg *CGRConfig) LazySanityCheck() { for _, cdrePrfl := range cfg.cdrsCfg.OnlineCDRExports { diff --git a/config/eescfg.go b/config/eescfg.go index bea954fe0..794aaefce 100644 --- a/config/eescfg.go +++ b/config/eescfg.go @@ -205,6 +205,9 @@ func (eeC *EventExporterCfg) loadFromJsonCfg(jsnEec *EventExporterJsonCfg, separ case utils.MetaTrl: eeC.trailerFields = append(eeC.trailerFields, field) } + if strings.HasPrefix(field.GetPathSlice()[0], utils.MetaCache) { // special cache when loading fields that contains *cache in path + eeC.contentFields = append(eeC.contentFields, field) + } } } return diff --git a/data/conf/samples/ees/cgrates.json b/data/conf/samples/ees/cgrates.json index 960fa7070..8826234ae 100644 --- a/data/conf/samples/ees/cgrates.json +++ b/data/conf/samples/ees/cgrates.json @@ -298,6 +298,46 @@ {"tag": "Cost", "path": "*exp.Cost", "type": "*variable", "value": "~*req.Cost{*round:4}"}, ], }, + { + "id": "RouteExporter", // this exporter will set the Cost Account and RunID in cache so we can use it in other exports + "type": "*virt", + "tenant": "cgrates.org", + "attempts": 1, + "filters": ["*string:~*req.ExporterUsed:RouteExporter"], + "fields":[ + {"tag": "Cost", "path": "*cache[~*req.CGRID|~*req.RunID|-Cost]", "type": "*variable", + "value": "~*req.Cost", "rounding_decimals": 4}, + {"tag": "Account", "path": "*cache[~*req.CGRID|~*req.RunID|-Account]", "type": "*variable", "value": "~*req.Account"}, + {"tag": "RunID", "path": "*cache[~*req.CGRID|~*req.RunID|-RunID]", "type": "*variable", "value": "~*req.RunID"}, + {"tag": "CustomVariable", "path": "*cache[~*req.CGRID|~*req.RunID|-CustomVariable]", + "type": "*variable", "value": "CustomValue"} + ], + }, + { + "id": "CSVExporterFromVirt", + "type": "*file_csv", + "export_path": "/tmp/testCSVfromVirt", + "tenant": "cgrates.org", + "flags": ["*attributes"], + "attribute_context": "customContext", + "attempts": 1, + "field_separator": ",", + "filters": ["*string:~*req.ExporterUsed:CSVExporterFromVirt"], + "fields":[ + {"tag": "CGRID", "path": "*exp.CGRID", "type": "*variable", "value": "~*req.CGRID"}, + {"tag": "RunID", "path": "*exp.RunID", "type": "*variable", "value": "~*req.RunID"}, + {"tag": "OriginID", "path": "*exp.OriginID", "type": "*variable", "value": "~*req.OriginID"}, + {"tag": "Tenant", "path": "*exp.Tenant", "type": "*variable", "value": "~*req.Tenant"}, + {"tag": "Account", "path": "*exp.Account", "type": "*variable", "value": "~*req.Account"}, + {"tag": "Cost", "path": "*exp.Cost", "type": "*variable", "value": "~*req.Cost", "rounding_decimals": 4}, + {"tag": "SupplierCustomVariable","filters": ["*exists:*cache[~*req.CGRID|~*req.RunID|-CustomVariable]:"], + "path": "*exp.SupplierCustomVariable", "type": "*variable", "value": "~*cache[~*req.CGRID|~*req.RunID|-CustomVariable]"}, + {"tag": "SupplierCost","filters": ["*exists:*cache[~*req.CGRID|~*req.RunID|-Cost]:"], + "path": "*exp.SupplierCost", "type": "*variable", "value": "~*cache[~*req.CGRID|~*req.RunID|-Cost]"}, + {"tag": "SupplierRun","filters": ["*exists:*cache[~*req.CGRID|~*req.RunID|-RunID]:"], + "path": "*exp.SupplierRun", "type": "*variable", "value": "~*cache[~*req.CGRID|~*req.RunID|-RunID]"}, + ], + } ] }, diff --git a/ees/ee.go b/ees/ee.go index aa3599ec0..9413ea0a1 100644 --- a/ees/ee.go +++ b/ees/ee.go @@ -43,6 +43,8 @@ func NewEventExporter(cgrCfg *config.CGRConfig, cfgIdx int, filterS *engine.Filt return NewHTTPPostEe(cgrCfg, cfgIdx, filterS, dc) case utils.MetaHTTPjsonMap, utils.MetaAMQPjsonMap, utils.MetaAMQPV1jsonMap, utils.MetaSQSjsonMap, utils.MetaKafkajsonMap, utils.MetaS3jsonMap: return NewHTTPJsonMapEe(cgrCfg, cfgIdx, filterS, dc) + case utils.MetaVirt: + return NewVirtualExporter(cgrCfg, cfgIdx, filterS, dc) default: return nil, fmt.Errorf("unsupported exporter type: <%s>", cgrCfg.EEsCfg().Exporters[cfgIdx].Type) } diff --git a/ees/eereq.go b/ees/eereq.go index 3a92aa4aa..8c60f1ff0 100644 --- a/ees/eereq.go +++ b/ees/eereq.go @@ -127,11 +127,12 @@ func (eeR *EventExporterRequest) FieldAsString(fldPath []string) (val string, er func (eeR *EventExporterRequest) SetFields(tplFlds []*config.FCTemplate) (err error) { for _, tplFld := range tplFlds { if pass, err := eeR.filterS.Pass(eeR.tnt, - tplFld.Filters, eeR); err != nil { + tplFld.Filters, eeR.dynamicProvider); err != nil { return err } else if !pass { continue } + var out interface{} out, err = eeR.ParseField(tplFld) if err != nil { @@ -157,7 +158,6 @@ func (eeR *EventExporterRequest) SetFields(tplFlds []*config.FCTemplate) (err er } else { itmPath = fullPath.PathItems.Slice()[1:] } - nMItm := &config.NMItem{Data: out, Path: itmPath, Config: tplFld} switch tplFld.Type { case utils.META_COMPOSED: diff --git a/ees/lib_test.go b/ees/lib_test.go index 52dddd657..a78730eea 100644 --- a/ees/lib_test.go +++ b/ees/lib_test.go @@ -49,7 +49,7 @@ func newRPCClient(cfg *config.ListenCfg) (c *rpc.Client, err error) { } } -var exportPath = []string{"/tmp/testCSV", "/tmp/testComposedCSV", "/tmp/testFWV", "/tmp/testCSVMasked"} +var exportPath = []string{"/tmp/testCSV", "/tmp/testComposedCSV", "/tmp/testFWV", "/tmp/testCSVMasked", "/tmp/testCSVfromVirt"} func testCreateDirectory(t *testing.T) { for _, dir := range exportPath { diff --git a/ees/virtualEe.go b/ees/virtualEe.go new file mode 100644 index 000000000..fb3cf2dee --- /dev/null +++ b/ees/virtualEe.go @@ -0,0 +1,118 @@ +/* +Real-time Online/Offline Charging System (OerS) 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 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package ees + +import ( + "sync" + "time" + + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +func NewVirtualExporter(cgrCfg *config.CGRConfig, cfgIdx int, filterS *engine.FilterS, + dc utils.MapStorage) (vEe *VirtualEe, err error) { + dc[utils.ExportID] = cgrCfg.EEsCfg().Exporters[cfgIdx].ID + vEe = &VirtualEe{id: cgrCfg.EEsCfg().Exporters[cfgIdx].ID, + cgrCfg: cgrCfg, cfgIdx: cfgIdx, filterS: filterS, dc: dc} + err = vEe.init() + return +} + +// VirtualEe implements EventExporter interface for .csv files +type VirtualEe struct { + id string + cgrCfg *config.CGRConfig + cfgIdx int // index of config instance within ERsCfg.Readers + filterS *engine.FilterS + sync.RWMutex + dc utils.MapStorage +} + +// init will create all the necessary dependencies, including opening the file +func (vEe *VirtualEe) init() (err error) { + return +} + +// ID returns the identificator of this exporter +func (vEe *VirtualEe) ID() string { + return vEe.id +} + +// OnEvicted implements EventExporter, doing the cleanup before exit +func (vEe *VirtualEe) OnEvicted(_ string, _ interface{}) { + return +} + +// ExportEvent implements EventExporter +func (vEe *VirtualEe) ExportEvent(cgrEv *utils.CGREvent) (err error) { + vEe.Lock() + defer vEe.Unlock() + + vEe.dc[utils.NumberOfEvents] = vEe.dc[utils.NumberOfEvents].(int) + 1 + + req := utils.MapStorage{} + for k, v := range cgrEv.Event { + req[k] = v + } + eeReq := NewEventExporterRequest(req, vEe.dc, cgrEv.Tenant, vEe.cgrCfg.GeneralCfg().DefaultTimezone, + vEe.filterS) + if err = eeReq.SetFields(vEe.cgrCfg.EEsCfg().Exporters[vEe.cfgIdx].ContentFields()); err != nil { + vEe.dc[utils.NegativeExports].(utils.StringSet).Add(cgrEv.ID) + return + } + if aTime, err := cgrEv.FieldAsTime(utils.AnswerTime, vEe.cgrCfg.GeneralCfg().DefaultTimezone); err == nil { + if vEe.dc[utils.FirstEventATime].(time.Time).IsZero() || vEe.dc[utils.FirstEventATime].(time.Time).Before(aTime) { + vEe.dc[utils.FirstEventATime] = aTime + } + if aTime.After(vEe.dc[utils.LastEventATime].(time.Time)) { + vEe.dc[utils.LastEventATime] = aTime + } + } + if oID, err := cgrEv.FieldAsInt64(utils.OrderID); err == nil { + if vEe.dc[utils.FirstExpOrderID].(int64) > oID || vEe.dc[utils.FirstExpOrderID].(int64) == 0 { + vEe.dc[utils.FirstExpOrderID] = oID + } + if vEe.dc[utils.LastExpOrderID].(int64) < oID { + vEe.dc[utils.LastExpOrderID] = oID + } + } + if cost, err := cgrEv.FieldAsFloat64(utils.Cost); err == nil { + vEe.dc[utils.TotalCost] = vEe.dc[utils.TotalCost].(float64) + cost + } + if tor, err := cgrEv.FieldAsString(utils.ToR); err == nil { + if usage, err := cgrEv.FieldAsDuration(utils.Usage); err == nil { + switch tor { + case utils.VOICE: + vEe.dc[utils.TotalDuration] = vEe.dc[utils.TotalDuration].(time.Duration) + usage + case utils.SMS: + vEe.dc[utils.TotalSMSUsage] = vEe.dc[utils.TotalSMSUsage].(time.Duration) + usage + case utils.MMS: + vEe.dc[utils.TotalMMSUsage] = vEe.dc[utils.TotalMMSUsage].(time.Duration) + usage + case utils.GENERIC: + vEe.dc[utils.TotalGenericUsage] = vEe.dc[utils.TotalGenericUsage].(time.Duration) + usage + case utils.DATA: + vEe.dc[utils.TotalDataUsage] = vEe.dc[utils.TotalDataUsage].(time.Duration) + usage + } + } + } + vEe.dc[utils.PositiveExports].(utils.StringSet).Add(cgrEv.ID) + return +} diff --git a/ees/virtual_ee_it_test.go b/ees/virtual_ee_it_test.go new file mode 100644 index 000000000..c8d27200b --- /dev/null +++ b/ees/virtual_ee_it_test.go @@ -0,0 +1,192 @@ +// +build integration + +/* +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 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package ees + +import ( + "io/ioutil" + "net/rpc" + "os" + "path" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/cgrates/cgrates/utils" + + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" +) + +var ( + virtConfigDir string + virtCfgPath string + virtCfg *config.CGRConfig + virtRpc *rpc.Client + + sTestsVirt = []func(t *testing.T){ + testCreateDirectory, + testVirtLoadConfig, + testVirtResetDataDB, + testVirtResetStorDb, + testVirtStartEngine, + testVirtRPCConn, + testVirtExportSupplierEvent, + testVirtExportEvents, + testVirtVerifyExports, + testStopCgrEngine, + testCleanDirectory, + } +) + +func TestVirtualExport(t *testing.T) { + virtConfigDir = "ees" + for _, stest := range sTestsVirt { + t.Run(virtConfigDir, stest) + } +} + +func testVirtLoadConfig(t *testing.T) { + var err error + virtCfgPath = path.Join(*dataDir, "conf", "samples", virtConfigDir) + if virtCfg, err = config.NewCGRConfigFromPath(virtCfgPath); err != nil { + t.Error(err) + } +} + +func testVirtResetDataDB(t *testing.T) { + if err := engine.InitDataDb(virtCfg); err != nil { + t.Fatal(err) + } +} + +func testVirtResetStorDb(t *testing.T) { + if err := engine.InitStorDb(virtCfg); err != nil { + t.Fatal(err) + } +} + +func testVirtStartEngine(t *testing.T) { + if _, err := engine.StopStartEngine(virtCfgPath, *waitRater); err != nil { + t.Fatal(err) + } +} + +func testVirtRPCConn(t *testing.T) { + var err error + virtRpc, err = newRPCClient(virtCfg.ListenCfg()) + if err != nil { + t.Fatal(err) + } +} + +func testVirtExportSupplierEvent(t *testing.T) { + supplierEvent := &utils.CGREventWithOpts{ + CGREvent: &utils.CGREvent{ + Tenant: "cgrates.org", + ID: "supplierEvent", + Time: utils.TimePointer(time.Now()), + Event: map[string]interface{}{ + utils.CGRID: utils.Sha1("dsafdsaf", time.Unix(1383813745, 0).UTC().String()), + utils.ToR: utils.VOICE, + utils.OriginID: "dsafdsaf", + utils.OriginHost: "192.168.1.1", + utils.RequestType: utils.META_RATED, + utils.Tenant: "cgrates.org", + utils.Category: "call", + utils.Account: "1001", + utils.Subject: "1001", + utils.Destination: "1002", + utils.SetupTime: time.Unix(1383813745, 0).UTC(), + utils.AnswerTime: time.Unix(1383813746, 0).UTC(), + utils.Usage: time.Duration(10) * time.Second, + utils.RunID: "SupplierRun", + utils.Cost: 1.23, + "ExporterUsed": "RouteExporter", + }, + }, + } + + var reply string + if err := virtRpc.Call(utils.EventExporterSv1ProcessEvent, supplierEvent, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Errorf("Expected %+v, received: %+v", utils.OK, reply) + } + time.Sleep(10 * time.Millisecond) +} + +func testVirtExportEvents(t *testing.T) { + eventVoice := &utils.CGREventWithOpts{ + CGREvent: &utils.CGREvent{ + Tenant: "cgrates.org", + ID: "voiceEvent", + Time: utils.TimePointer(time.Now()), + Event: map[string]interface{}{ + utils.CGRID: utils.Sha1("dsafdsaf", time.Unix(1383813745, 0).UTC().String()), + utils.ToR: utils.VOICE, + utils.OriginID: "dsafdsaf", + utils.OriginHost: "192.168.1.1", + utils.RequestType: utils.META_RATED, + utils.Tenant: "cgrates.org", + utils.Category: "call", + utils.Account: "1001", + utils.Subject: "1001", + utils.Destination: "1002", + utils.SetupTime: time.Unix(1383813745, 0).UTC(), + utils.AnswerTime: time.Unix(1383813746, 0).UTC(), + utils.Usage: time.Duration(10) * time.Second, + utils.RunID: "SupplierRun", + utils.Cost: 1.01, + "ExporterUsed": "CSVExporterFromVirt", + }, + }, + } + var reply string + if err := virtRpc.Call(utils.EventExporterSv1ProcessEvent, eventVoice, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Errorf("Expected %+v, received: %+v", utils.OK, reply) + } + time.Sleep(1 * time.Second) +} + +func testVirtVerifyExports(t *testing.T) { + var files []string + err := filepath.Walk("/tmp/testCSVfromVirt/", func(path string, info os.FileInfo, err error) error { + if strings.HasSuffix(path, utils.CSVSuffix) { + files = append(files, path) + } + return nil + }) + if err != nil { + t.Error(err) + } + if len(files) != 1 { + t.Errorf("Expected %+v, received: %+v", 1, len(files)) + } + eCnt := "dbafe9c8614c785a65aabd116dd3959c3c56f7f6,SupplierRun,dsafdsaf,cgrates.org,1001,1.01,CustomValue,1.23,SupplierRun\n" + if outContent1, err := ioutil.ReadFile(files[0]); err != nil { + t.Error(err) + } else if eCnt != string(outContent1) { + t.Errorf("Expecting: \n<%q>, \nreceived: \n<%q>", eCnt, string(outContent1)) + } +} diff --git a/engine/filters.go b/engine/filters.go index 0bdb404c4..fd0b167d2 100644 --- a/engine/filters.go +++ b/engine/filters.go @@ -549,7 +549,7 @@ func (dDP *dynamicDP) RemoteHost() net.Addr { var initialDPPrefixes = utils.NewStringSet([]string{utils.MetaReq, utils.MetaVars, utils.MetaCgreq, utils.MetaCgrep, utils.MetaRep, utils.MetaCGRAReq, - utils.MetaAct, utils.MetaEC}) + utils.MetaAct, utils.MetaEC, utils.MetaCache}) func (dDP *dynamicDP) FieldAsInterface(fldPath []string) (val interface{}, err error) { if len(fldPath) == 0 { diff --git a/utils/consts.go b/utils/consts.go index 174776325..f46688ecd 100755 --- a/utils/consts.go +++ b/utils/consts.go @@ -396,6 +396,7 @@ const ( ActionsPoster = "act" CDRPoster = "cdr" MetaFileCSV = "*file_csv" + MetaVirt = "*virt" MetaFileFWV = "*file_fwv" MetaFScsv = "*freeswitch_csv" Accounts = "Accounts" diff --git a/utils/dynamicdataprovider.go b/utils/dynamicdataprovider.go index 877100c54..f5cea2d43 100644 --- a/utils/dynamicdataprovider.go +++ b/utils/dynamicdataprovider.go @@ -43,7 +43,7 @@ func (ddp *DynamicDataProvider) FieldAsInterface(fldPath []string) (out interfac return val, nil } var newPath string - if newPath, err = ddp.proccesFieldPath(path); err != nil { // proccess the path + if newPath, err = ddp.processFieldPathForSet(path); err != nil { // proccess the path return } if newPath == EmptyString { // no new path means no dynamic path so just take the value from the data provider @@ -58,64 +58,10 @@ func (ddp *DynamicDataProvider) FieldAsInterface(fldPath []string) (out interfac return } -func (ddp *DynamicDataProvider) proccesFieldPath(fldPath string) (newPath string, err error) { - idx := strings.Index(fldPath, IdxStart) - if idx == -1 { - return // no proccessing requred - } - newPath = fldPath[:idx+1] // add the first path of the path with the "[" included - for idx != -1 { // stop when we do not find any "[" - fldPath = fldPath[idx+1:] // move the path to the begining of the index - nextBeginIdx := strings.Index(fldPath, IdxStart) // get the next "[" if any - nextEndIdx := strings.Index(fldPath, IdxEnd) // get the next "]" if any - if nextEndIdx == -1 { // no end index found so return error - err = ErrWrongPath - newPath = EmptyString - return - } - - // parse the rest of the field path until we match the [ ] - bIdx, eIdx := nextBeginIdx, nextEndIdx - for nextBeginIdx != -1 && nextBeginIdx < nextEndIdx { // do this until no new [ is found or the next begining [ is after the end ] - nextBeginIdx = strings.Index(fldPath[bIdx+1:], IdxStart) // get the next "[" if any - nextEndIdx = strings.Index(fldPath[eIdx+1:], IdxEnd) // get the next "]" if any - if nextEndIdx == -1 { // no end index found so return error - err = ErrWrongPath - newPath = EmptyString - return - } - if nextBeginIdx == -1 { // if no index found do not increment but replace it - bIdx = -1 - } else { - bIdx += nextBeginIdx + 1 - } - // increment the indexes - eIdx += nextEndIdx + 1 - } - var val string - for _, path := range strings.Split(fldPath[:eIdx], PipeSep) { // proccess the found path - var iface interface{} - if iface, err = DPDynamicInterface(path, ddp); err != nil { - newPath = EmptyString - return - } - val += IfaceAsString(iface) // compose the value - } - if bIdx == -1 { // if is the last ocurence add the rest of the path and exit - newPath += val + fldPath[eIdx:] - } else { - // else just add until the next [ - newPath += val + fldPath[eIdx:bIdx+1] - } - idx = bIdx - } - return -} - // GetFullFieldPath returns the full path for the func (ddp *DynamicDataProvider) GetFullFieldPath(fldPath string) (fpath *FullPath, err error) { var newPath string - if newPath, err = ddp.proccesFieldPathForSet(fldPath); err != nil || newPath == EmptyString { + if newPath, err = ddp.processFieldPathForSet(fldPath); err != nil || newPath == EmptyString { return } fpath = &FullPath{ @@ -136,7 +82,7 @@ func (ddp DynamicDataProvider) FieldAsString(fldPath []string) (str string, err } // does the same thing as ... but replaces [ with . if the value between [] is dynamic -func (ddp *DynamicDataProvider) proccesFieldPathForSet(fldPath string) (newPath string, err error) { +func (ddp *DynamicDataProvider) processFieldPathForSet(fldPath string) (newPath string, err error) { idx := strings.Index(fldPath, IdxStart) if idx == -1 { return // no proccessing requred diff --git a/utils/dynamicdataprovider_test.go b/utils/dynamicdataprovider_test.go index aa8d37ca0..c79f50a1d 100644 --- a/utils/dynamicdataprovider_test.go +++ b/utils/dynamicdataprovider_test.go @@ -18,56 +18,11 @@ along with this program. If not, see package utils import ( + "fmt" "strings" "testing" ) -func TestDynamicDataProviderProccesFieldPath(t *testing.T) { - dp := MapStorage{ - MetaCgrep: MapStorage{ - "Stir": MapStorage{ - "CHRG_ROUTE1_END": "Identity1", - "CHRG_ROUTE2_END": "Identity2", - "CHRG_ROUTE3_END": "Identity3", - "CHRG_ROUTE4_END": "Identity4", - }, - "Routes": MapStorage{ - "SortedRoutes": []MapStorage{ - {"ID": "ROUTE1"}, - {"ID": "ROUTE2"}, - {"ID": "ROUTE3"}, - {"ID": "ROUTE4"}, - }, - }, - "BestRoute": 0, - }, - } - ddp := NewDynamicDataProvider(dp) - newpath, err := ddp.proccesFieldPath("~*cgrep.Stir[CHRG_|~*cgrep.Routes.SortedRoutes[1].ID|_END].Something[CHRG_|~*cgrep.Routes.SortedRoutes[~*cgrep.BestRoute].ID|_END]") - if err != nil { - t.Fatal(err) - } - expectedPath := "~*cgrep.Stir[CHRG_ROUTE2_END].Something[CHRG_ROUTE1_END]" - if newpath != expectedPath { - t.Errorf("Expected: %q,received %q", expectedPath, newpath) - } - _, err = ddp.proccesFieldPath("~*cgrep.Stir[CHRG_|~*cgrep.Routes.SortedRoutes[1].ID|_END].Something[CHRG_|~*cgrep.Routes.SortedRoutes[~*cgrep.BestRoute].ID|_END") - if err != ErrWrongPath { - t.Errorf("Expected error %s received %v", ErrWrongPath, err) - } - - _, err = ddp.proccesFieldPath("~*cgrep.Stir[CHRG_") - if err != ErrWrongPath { - t.Errorf("Expected error %s received %v", ErrWrongPath, err) - } - - _, err = ddp.proccesFieldPath("~*cgrep.Stir[CHRG_|~*cgrep.Routes.SortedRoutes[1].ID2|_END]") - if err != ErrNotFound { - t.Errorf("Expected error %s received %v", ErrNotFound, err) - } - -} - func TestDynamicDataProviderFieldAsInterface(t *testing.T) { dp := MapStorage{ MetaCgrep: MapStorage{ @@ -163,7 +118,7 @@ func TestDynamicDataProviderProccesFieldPath2(t *testing.T) { }, } ddp := NewDynamicDataProvider(dp) - newpath, err := ddp.proccesFieldPathForSet("~*cgrep.Stir[CHRG_|~*cgrep.Routes.SortedRoutes[1].ID|_END].Something[CHRG_|~*cgrep.Routes.SortedRoutes[~*cgrep.BestRoute].ID|_END]") + newpath, err := ddp.processFieldPathForSet("~*cgrep.Stir[CHRG_|~*cgrep.Routes.SortedRoutes[1].ID|_END].Something[CHRG_|~*cgrep.Routes.SortedRoutes[~*cgrep.BestRoute].ID|_END]") if err != nil { t.Fatal(err) } @@ -171,21 +126,21 @@ func TestDynamicDataProviderProccesFieldPath2(t *testing.T) { if newpath != expectedPath { t.Errorf("Expected: %q,received %q", expectedPath, newpath) } - _, err = ddp.proccesFieldPathForSet("~*cgrep.Stir[CHRG_|~*cgrep.Routes.SortedRoutes[1].ID|_END].Something[CHRG_|~*cgrep.Routes.SortedRoutes[~*cgrep.BestRoute].ID|_END") + _, err = ddp.processFieldPathForSet("~*cgrep.Stir[CHRG_|~*cgrep.Routes.SortedRoutes[1].ID|_END].Something[CHRG_|~*cgrep.Routes.SortedRoutes[~*cgrep.BestRoute].ID|_END") if err != ErrWrongPath { t.Errorf("Expected error %s received %v", ErrWrongPath, err) } - _, err = ddp.proccesFieldPathForSet("~*cgrep.Stir[CHRG_") + _, err = ddp.processFieldPathForSet("~*cgrep.Stir[CHRG_") if err != ErrWrongPath { t.Errorf("Expected error %s received %v", ErrWrongPath, err) } - _, err = ddp.proccesFieldPathForSet("~*cgrep.Stir[CHRG_|~*cgrep.Routes.SortedRoutes[1].ID2|_END]") + _, err = ddp.processFieldPathForSet("~*cgrep.Stir[CHRG_|~*cgrep.Routes.SortedRoutes[1].ID2|_END]") if err != ErrNotFound { t.Errorf("Expected error %s received %v", ErrNotFound, err) } - newpath, err = ddp.proccesFieldPathForSet("~*cgrep.Stir[1]") + newpath, err = ddp.processFieldPathForSet("~*cgrep.Stir[1]") if err != nil { t.Fatal(err) } @@ -289,4 +244,13 @@ func TestDynamicDataProviderGetFullFieldPath(t *testing.T) { if newpath != nil { t.Errorf("Expected: %v,received %q", nil, newpath) } + + newpath, err = ddp.GetFullFieldPath("*cgrep.Stir[CHRG_|~*cgrep.Routes.SortedRoutes[~*cgrep.BestRoute].ID|_END]") + if err != nil { + t.Fatal(err) + } + if newpath == nil { + t.Errorf("Expected: %v,received %q", nil, newpath) + } + fmt.Println(*newpath) }