Skip to content

Commit

Permalink
Added DynamicDataProvider
Browse files Browse the repository at this point in the history
  • Loading branch information
Trial97 authored and danbogos committed May 20, 2020
1 parent c5b472c commit da93441
Show file tree
Hide file tree
Showing 12 changed files with 578 additions and 92 deletions.
18 changes: 9 additions & 9 deletions engine/attributes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1943,7 +1943,7 @@ func TestProcessAttributeVariable(t *testing.T) {
if err != nil {
t.Errorf("Error: %+v", err)
}
clnEv := ev.Clone()
clnEv := ev.CGREvent.Clone()
clnEv.Event["Field2"] = "Val2"
eRply := &AttrSProcessEventReply{
MatchedProfiles: []string{"ATTR_VARIABLE"},
Expand Down Expand Up @@ -2001,7 +2001,7 @@ func TestProcessAttributeComposed(t *testing.T) {
if err != nil {
t.Errorf("Error: %+v", err)
}
clnEv := ev.Clone()
clnEv := ev.CGREvent.Clone()
clnEv.Event["Field2"] = "Val2Concatenated"
eRply := &AttrSProcessEventReply{
MatchedProfiles: []string{"ATTR_COMPOSED"},
Expand Down Expand Up @@ -2054,7 +2054,7 @@ func TestProcessAttributeUsageDifference(t *testing.T) {
if err != nil {
t.Errorf("Error: %+v", err)
}
clnEv := ev.Clone()
clnEv := ev.CGREvent.Clone()
clnEv.Event["Field2"] = "1h0m0s"
eRply := &AttrSProcessEventReply{
MatchedProfiles: []string{"ATTR_USAGE_DIFF"},
Expand Down Expand Up @@ -2107,7 +2107,7 @@ func TestProcessAttributeSum(t *testing.T) {
if err != nil {
t.Errorf("Error: %+v", err)
}
clnEv := ev.Clone()
clnEv := ev.CGREvent.Clone()
clnEv.Event["Field2"] = "16"
eRply := &AttrSProcessEventReply{
MatchedProfiles: []string{"ATTR_SUM"},
Expand Down Expand Up @@ -2160,7 +2160,7 @@ func TestProcessAttributeDiff(t *testing.T) {
if err != nil {
t.Errorf("Error: %+v", err)
}
clnEv := ev.Clone()
clnEv := ev.CGREvent.Clone()
clnEv.Event["Field2"] = "39"
eRply := &AttrSProcessEventReply{
MatchedProfiles: []string{"ATTR_DIFF"},
Expand Down Expand Up @@ -2213,7 +2213,7 @@ func TestProcessAttributeMultiply(t *testing.T) {
if err != nil {
t.Errorf("Error: %+v", err)
}
clnEv := ev.Clone()
clnEv := ev.CGREvent.Clone()
clnEv.Event["Field2"] = "2750"
eRply := &AttrSProcessEventReply{
MatchedProfiles: []string{"ATTR_MULTIPLY"},
Expand Down Expand Up @@ -2266,7 +2266,7 @@ func TestProcessAttributeDivide(t *testing.T) {
if err != nil {
t.Errorf("Error: %+v", err)
}
clnEv := ev.Clone()
clnEv := ev.CGREvent.Clone()
clnEv.Event["Field2"] = "2.75"
eRply := &AttrSProcessEventReply{
MatchedProfiles: []string{"ATTR_DIVIDE"},
Expand Down Expand Up @@ -2319,7 +2319,7 @@ func TestProcessAttributeValueExponent(t *testing.T) {
if err != nil {
t.Errorf("Error: %+v", err)
}
clnEv := ev.Clone()
clnEv := ev.CGREvent.Clone()
clnEv.Event["Field2"] = "50000"
eRply := &AttrSProcessEventReply{
MatchedProfiles: []string{"ATTR_VAL_EXP"},
Expand Down Expand Up @@ -2372,7 +2372,7 @@ func TestProcessAttributeUnixTimeStamp(t *testing.T) {
if err != nil {
t.Errorf("Error: %+v", err)
}
clnEv := ev.Clone()
clnEv := ev.CGREvent.Clone()
clnEv.Event["Field2"] = "1388415601"
eRply := &AttrSProcessEventReply{
MatchedProfiles: []string{"ATTR_UNIX_TIMESTAMP"},
Expand Down
2 changes: 1 addition & 1 deletion engine/cdrs.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ func (cdrS *CDRServer) rateCDR(cdr *CDRWithArgDispatcher) ([]*CDR, error) {
}
if len(smCosts) != 0 { // Cost retrieved from SMCost table
for _, smCost := range smCosts {
cdrClone := cdr.Clone()
cdrClone := cdr.CDR.Clone()
cdrClone.OriginID = smCost.OriginID
if cdr.Usage == 0 {
cdrClone.Usage = smCost.Usage
Expand Down
1 change: 1 addition & 0 deletions engine/model_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2584,6 +2584,7 @@ func TestTPRoutesAsTPRouteProfile(t *testing.T) {
sort.Slice(rcvRev[0].Routes, func(i, j int) bool {
return strings.Compare(rcvRev[0].Routes[i].ID, rcvRev[0].Routes[j].ID) < 0
})
sort.Strings(rcvRev[0].SortingParameters)
if !reflect.DeepEqual(rcvRev, expPrfRev) {
t.Errorf("Expecting: %+v,\nReceived: %+v", utils.ToJSON(expPrfRev), utils.ToJSON(rcvRev))
}
Expand Down
16 changes: 16 additions & 0 deletions utils/apitpdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -1344,11 +1344,27 @@ type SessionFilter struct {
*ArgDispatcher
}

// ArgDispatcher the basic information for dispatcher
type ArgDispatcher struct {
APIKey *string
RouteID *string
}

// Clone returns a copy of the ArgDispatcher
func (arg *ArgDispatcher) Clone() (clned *ArgDispatcher) {
if arg == nil {
return
}
clned = new(ArgDispatcher)
if arg.APIKey != nil {
clned.APIKey = StringPointer(*arg.APIKey)
}
if arg.RouteID != nil {
clned.RouteID = StringPointer(*arg.RouteID)
}
return
}

type RatingPlanCostArg struct {
RatingPlanIDs []string
Destination string
Expand Down
9 changes: 3 additions & 6 deletions utils/cgrevent.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,7 @@ func (ev *CGREventWithArgDispatcher) Clone() (clned *CGREventWithArgDispatcher)
clned.CGREvent = ev.CGREvent.Clone()
}
if ev.ArgDispatcher != nil {
clned.ArgDispatcher = new(ArgDispatcher)
*clned.ArgDispatcher = *ev.ArgDispatcher
clned.ArgDispatcher = ev.ArgDispatcher.Clone()
}
return
}
Expand Down Expand Up @@ -268,6 +267,7 @@ func ExtractArgsFromOpts(ev map[string]interface{}, dispatcherFlag, consumeRoute
return
}

// Clone return a copy of the CGREventWithOpts
func (ev *CGREventWithOpts) Clone() (clned *CGREventWithOpts) {
if ev == nil {
return
Expand All @@ -276,10 +276,7 @@ func (ev *CGREventWithOpts) Clone() (clned *CGREventWithOpts) {
if ev.CGREvent != nil {
clned.CGREvent = ev.CGREvent.Clone()
}
if ev.ArgDispatcher != nil {
clned.ArgDispatcher = new(ArgDispatcher)
*clned.ArgDispatcher = *ev.ArgDispatcher
}
clned.ArgDispatcher = ev.ArgDispatcher.Clone()
if ev.Opts != nil {
clned.Opts = make(map[string]interface{})
for opt, val := range ev.Opts {
Expand Down
91 changes: 91 additions & 0 deletions utils/cgrevent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -517,5 +517,96 @@ func TestCGREventWithArgDispatcherClone(t *testing.T) {
if reflect.DeepEqual(cgrEventWithArgDispatcher.ArgDispatcher, rcv.ArgDispatcher) {
t.Errorf("Expected to be different")
}
}

func TestCGREventWithOptsClone(t *testing.T) {
//empty check
cgrEventWithOpts := new(CGREventWithOpts)
rcv := cgrEventWithOpts.Clone()
if !reflect.DeepEqual(cgrEventWithOpts, rcv) {
t.Errorf("Expecting: %+v, received: %+v", cgrEventWithOpts, rcv)
}
//nil check
cgrEventWithOpts = nil
rcv = cgrEventWithOpts.Clone()
if !reflect.DeepEqual(cgrEventWithOpts, rcv) {
t.Errorf("Expecting: %+v, received: %+v", cgrEventWithOpts, rcv)
}
//normal check
now := time.Now()
cgrEventWithOpts = &CGREventWithOpts{
CGREvent: &CGREvent{
Tenant: "cgrates.org",
ID: "IDtest",
Time: &now,
Event: map[string]interface{}{
"test1": 1,
"test2": 2,
"test3": 3,
},
},
ArgDispatcher: &ArgDispatcher{
APIKey: StringPointer("api1"),
RouteID: StringPointer("route1"),
},
Opts: map[string]interface{}{
"Context": MetaSessionS,
},
}
rcv = cgrEventWithOpts.Clone()
if !reflect.DeepEqual(cgrEventWithOpts, rcv) {
t.Errorf("Expecting: %+v, received: %+v", cgrEventWithOpts, rcv)
}
//check vars
rcv.ArgDispatcher = &ArgDispatcher{
APIKey: StringPointer("apikey"),
RouteID: StringPointer("routeid"),
}
if reflect.DeepEqual(cgrEventWithOpts.ArgDispatcher, rcv.ArgDispatcher) {
t.Errorf("Expected to be different")
}

}

func TestCGREventFieldAsInt64(t *testing.T) {
se := &CGREvent{
Tenant: "cgrates.org",
ID: "supplierEvent1",
Event: map[string]interface{}{
AnswerTime: time.Now(),
"supplierprofile1": "Supplier",
"UsageInterval": "54",
"PddInterval": "1s",
"Weight": 20,
},
}
answ, err := se.FieldAsInt64("UsageInterval")
if err != nil {
t.Error(err)
}
if answ != int64(54) {
t.Errorf("Expecting: %+v, received: %+v", se.Event["UsageInterval"], answ)
}
answ, err = se.FieldAsInt64("Weight")
if err != nil {
t.Error(err)
}
if answ != int64(20) {
t.Errorf("Expecting: %+v, received: %+v", se.Event["Weight"], answ)
}
answ, err = se.FieldAsInt64("PddInterval")
if err == nil || err.Error() != `strconv.ParseInt: parsing "1s": invalid syntax` {
t.Errorf("Expected %s, received %s", `strconv.ParseInt: parsing "1s": invalid syntax`, err)
}
if answ != 0 {
t.Errorf("Expecting: %+v, received: %+v", 0, answ)
}

if _, err := se.FieldAsInt64(AnswerTime); err == nil || !strings.HasPrefix(err.Error(), "cannot convert field") {
t.Errorf("Unexpected error : %+v", err)
}
if _, err := se.FieldAsInt64(Account); err == nil || err.Error() != ErrNotFound.Error() {
t.Errorf("Expected %s, received %s", ErrNotFound, err)
}
// }
}
5 changes: 5 additions & 0 deletions utils/coreutils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package utils
import (
"errors"
"fmt"
"math"
"reflect"
"sync"
"testing"
Expand Down Expand Up @@ -952,6 +953,10 @@ func TestClone(t *testing.T) {
} else if !reflect.DeepEqual(ifaceC, clndIface) {
t.Errorf("Expecting: %+v, received: %+v", ifaceC, clndIface)
}

if err := Clone(math.NaN, nil); err == nil {
t.Error("Expected error")
}
}

func TestFib(t *testing.T) {
Expand Down
113 changes: 113 additions & 0 deletions utils/dynamicdataprovider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
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 utils

import (
"strings"
)

// NewDynamicDataProvider constructs a dynamic data provider
func NewDynamicDataProvider(dp DataProvider) *DynamicDataProvider {
return &DynamicDataProvider{
DataProvider: dp,
cache: make(map[string]interface{}),
}
}

// DynamicDataProvider is a data source from multiple formats
type DynamicDataProvider struct {
DataProvider
cache map[string]interface{}
}

// FieldAsInterface to overwrite the FieldAsInterface function from the given DataProvider
func (ddp *DynamicDataProvider) FieldAsInterface(fldPath []string) (out interface{}, err error) {
path := strings.Join(fldPath, NestingSep) // join the path so we can check it in cache and parse it more easy
if val, has := ddp.cache[path]; has { // check if we have the path in cache
return val, nil
}
var newPath string
if newPath, err = ddp.proccesFieldPath(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
return ddp.DataProvider.FieldAsInterface(fldPath)
}
// split the new path and get that field
if out, err = ddp.DataProvider.FieldAsInterface(strings.Split(newPath, NestingSep)); err != nil {
return
}
// if no error save in cache the path
ddp.cache[path] = out
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
}
Loading

0 comments on commit da93441

Please sign in to comment.