Add support for *virt export type in EEs

This commit is contained in:
TeoV
2020-06-26 17:48:44 +03:00
parent 6c07c42949
commit 7a2817929f
12 changed files with 379 additions and 113 deletions

View File

@@ -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 {

View File

@@ -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

View File

@@ -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]"},
],
}
]
},

View File

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

View File

@@ -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:

View File

@@ -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 {

118
ees/virtualEe.go Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>
*/
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
}

192
ees/virtual_ee_it_test.go Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>
*/
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))
}
}

View File

@@ -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 {

View File

@@ -396,6 +396,7 @@ const (
ActionsPoster = "act"
CDRPoster = "cdr"
MetaFileCSV = "*file_csv"
MetaVirt = "*virt"
MetaFileFWV = "*file_fwv"
MetaFScsv = "*freeswitch_csv"
Accounts = "Accounts"

View File

@@ -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

View File

@@ -18,56 +18,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
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)
}