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)
}