Add support for *multiply and *divide in Agent and sync with AttributeS supported format fixes #1954

This commit is contained in:
TeoV
2020-02-17 09:20:29 +02:00
committed by Dan Christian Bogos
parent 43944fb7a7
commit 61a0aafd48
5 changed files with 302 additions and 0 deletions

View File

@@ -303,6 +303,26 @@ func (ar *AgentRequest) ParseField(
iFaceVals[i] = utils.StringToInterface(strVal)
}
out, err = utils.Difference(iFaceVals...)
case utils.MetaMultiply:
iFaceVals := make([]interface{}, len(cfgFld.Value))
for i, val := range cfgFld.Value {
strVal, err := val.ParseDataProvider(ar, utils.NestingSep)
if err != nil {
return "", err
}
iFaceVals[i] = utils.StringToInterface(strVal)
}
out, err = utils.Multiply(iFaceVals...)
case utils.MetaDivide:
iFaceVals := make([]interface{}, len(cfgFld.Value))
for i, val := range cfgFld.Value {
strVal, err := val.ParseDataProvider(ar, utils.NestingSep)
if err != nil {
return "", err
}
iFaceVals[i] = utils.StringToInterface(strVal)
}
out, err = utils.Divide(iFaceVals...)
case utils.MetaValueExponent:
if len(cfgFld.Value) != 2 {
return nil, fmt.Errorf("invalid arguments <%s> to %s",

View File

@@ -1381,6 +1381,94 @@ func TestAgReqParseFieldMetaDifference(t *testing.T) {
}
}
func TestAgReqParseFieldMetaMultiply(t *testing.T) {
//creater diameter message
m := diam.NewRequest(diam.CreditControl, 4, nil)
m.NewAVP("Session-Id", avp.Mbit, 0, datatype.UTF8String("simuhuawei;1449573472;00002"))
m.NewAVP("Subscription-Id", avp.Mbit, 0, &diam.GroupedAVP{
AVP: []*diam.AVP{
diam.NewAVP(450, avp.Mbit, 0, datatype.Enumerated(2)), // Subscription-Id-Type
diam.NewAVP(444, avp.Mbit, 0, datatype.UTF8String("208708000004")), // Subscription-Id-Data
diam.NewAVP(avp.ValueDigits, avp.Mbit, 0, datatype.Integer64(20000)),
}})
//create diameterDataProvider
dP := newDADataProvider(nil, m)
cfg, _ := config.NewDefaultCGRConfig()
data := engine.NewInternalDB(nil, nil, true, cfg.DataDbCfg().Items)
dm := engine.NewDataManager(data, config.CgrConfig().CacheCfg(), nil)
filterS := engine.NewFilterS(cfg, nil, dm)
//pass the data provider to agent request
agReq := NewAgentRequest(dP, nil, nil, nil, nil, "cgrates.org", "", filterS, nil, nil)
tplFlds := []*config.FCTemplate{
&config.FCTemplate{Tag: "Multiply", Filters: []string{},
Path: "Multiply", Type: utils.MetaMultiply,
Value: config.NewRSRParsersMustCompile("15;~*req.Session-Id", true, utils.INFIELD_SEP),
Mandatory: true},
}
if _, err := agReq.ParseField(tplFlds[0]); err == nil ||
err.Error() != `strconv.ParseInt: parsing "simuhuawei;1449573472;00002": invalid syntax` {
t.Error(err)
}
tplFlds = []*config.FCTemplate{
&config.FCTemplate{Tag: "Multiply", Filters: []string{},
Path: "Multiply", Type: utils.MetaMultiply,
Value: config.NewRSRParsersMustCompile("15;15", true, utils.INFIELD_SEP),
Mandatory: true},
}
expected := int64(225)
if out, err := agReq.ParseField(tplFlds[0]); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(out, expected) {
t.Errorf("expecting: <%+v>, %T received: <%+v> %T", expected, expected, out, out)
}
}
func TestAgReqParseFieldMetaDivide(t *testing.T) {
//creater diameter message
m := diam.NewRequest(diam.CreditControl, 4, nil)
m.NewAVP("Session-Id", avp.Mbit, 0, datatype.UTF8String("simuhuawei;1449573472;00002"))
m.NewAVP("Subscription-Id", avp.Mbit, 0, &diam.GroupedAVP{
AVP: []*diam.AVP{
diam.NewAVP(450, avp.Mbit, 0, datatype.Enumerated(2)), // Subscription-Id-Type
diam.NewAVP(444, avp.Mbit, 0, datatype.UTF8String("208708000004")), // Subscription-Id-Data
diam.NewAVP(avp.ValueDigits, avp.Mbit, 0, datatype.Integer64(20000)),
}})
//create diameterDataProvider
dP := newDADataProvider(nil, m)
cfg, _ := config.NewDefaultCGRConfig()
data := engine.NewInternalDB(nil, nil, true, cfg.DataDbCfg().Items)
dm := engine.NewDataManager(data, config.CgrConfig().CacheCfg(), nil)
filterS := engine.NewFilterS(cfg, nil, dm)
//pass the data provider to agent request
agReq := NewAgentRequest(dP, nil, nil, nil, nil, "cgrates.org", "", filterS, nil, nil)
tplFlds := []*config.FCTemplate{
&config.FCTemplate{Tag: "Divide", Filters: []string{},
Path: "Divide", Type: utils.MetaDivide,
Value: config.NewRSRParsersMustCompile("15;~*req.Session-Id", true, utils.INFIELD_SEP),
Mandatory: true},
}
if _, err := agReq.ParseField(tplFlds[0]); err == nil ||
err.Error() != `strconv.ParseInt: parsing "simuhuawei;1449573472;00002": invalid syntax` {
t.Error(err)
}
tplFlds = []*config.FCTemplate{
&config.FCTemplate{Tag: "Divide", Filters: []string{},
Path: "Divide", Type: utils.MetaDivide,
Value: config.NewRSRParsersMustCompile("15;3", true, utils.INFIELD_SEP),
Mandatory: true},
}
expected := int64(5)
if out, err := agReq.ParseField(tplFlds[0]); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(out, expected) {
t.Errorf("expecting: <%+v>, %T received: <%+v> %T", expected, expected, out, out)
}
}
func TestAgReqParseFieldMetaValueExponent(t *testing.T) {
//creater diameter message
m := diam.NewRequest(diam.CreditControl, 4, nil)

View File

@@ -223,6 +223,48 @@ func (alS *AttributeService) processEvent(args *AttrArgsProcessEvent) (
return nil, err
}
substitute = utils.IfaceAsString(ifaceSum)
case utils.MetaDifference:
iFaceVals := make([]interface{}, len(attribute.Value))
for i, val := range attribute.Value {
strVal, err := val.ParseDataProvider(evNm, utils.NestingSep)
if err != nil {
return nil, err
}
iFaceVals[i] = utils.StringToInterface(strVal)
}
ifaceSum, err := utils.Difference(iFaceVals...)
if err != nil {
return nil, err
}
substitute = utils.IfaceAsString(ifaceSum)
case utils.MetaMultiply:
iFaceVals := make([]interface{}, len(attribute.Value))
for i, val := range attribute.Value {
strVal, err := val.ParseDataProvider(evNm, utils.NestingSep)
if err != nil {
return nil, err
}
iFaceVals[i] = utils.StringToInterface(strVal)
}
ifaceSum, err := utils.Multiply(iFaceVals...)
if err != nil {
return nil, err
}
substitute = utils.IfaceAsString(ifaceSum)
case utils.MetaDivide:
iFaceVals := make([]interface{}, len(attribute.Value))
for i, val := range attribute.Value {
strVal, err := val.ParseDataProvider(evNm, utils.NestingSep)
if err != nil {
return nil, err
}
iFaceVals[i] = utils.StringToInterface(strVal)
}
ifaceSum, err := utils.Divide(iFaceVals...)
if err != nil {
return nil, err
}
substitute = utils.IfaceAsString(ifaceSum)
case utils.MetaValueExponent:
if len(attribute.Value) != 2 {
return nil, fmt.Errorf("invalid arguments <%s> to %s",
@@ -247,6 +289,16 @@ func (alS *AttributeService) processEvent(args *AttrArgsProcessEvent) (
}
substitute = strconv.FormatFloat(utils.Round(val*math.Pow10(exp),
config.CgrConfig().GeneralCfg().RoundingDecimals, utils.ROUNDING_MIDDLE), 'f', -1, 64)
case utils.MetaUnixTimestamp:
val, err := attribute.Value.ParseDataProvider(evNm, utils.NestingSep)
if err != nil {
return nil, err
}
t, err := utils.ParseTimeDetectLayout(val, alS.cgrcfg.GeneralCfg().DefaultTimezone)
if err != nil {
return nil, err
}
substitute = strconv.Itoa(int(t.Unix()))
default: // backwards compatible in case that Type is empty
substitute, err = attribute.Value.ParseDataProvider(evNm, utils.NestingSep)
}

View File

@@ -627,6 +627,98 @@ func Difference(items ...interface{}) (diff interface{}, err error) {
return
}
// Multiply attempts to multiply multiple items
// returns the result or error if not comparable
func Multiply(items ...interface{}) (mlt interface{}, err error) {
//we need at least 2 items to diff them
if len(items) < 2 {
return nil, ErrNotEnoughParameters
}
switch dt := items[0].(type) {
case float64:
mlt = dt
for _, item := range items[1:] {
if itmVal, err := IfaceAsFloat64(item); err != nil {
return nil, err
} else {
mlt = mlt.(float64) * itmVal
}
}
case int64:
mlt = dt
for _, item := range items[1:] {
if itmVal, err := IfaceAsInt64(item); err != nil {
return nil, err
} else {
mlt = mlt.(int64) * itmVal
}
}
case int:
// need explicit conversion for int
if firstItmVal, err := IfaceAsInt64(dt); err != nil {
return nil, err
} else {
mlt = firstItmVal
}
for _, item := range items[1:] {
if itmVal, err := IfaceAsInt64(item); err != nil {
return nil, err
} else {
mlt = mlt.(int64) * itmVal
}
}
default: // unsupported comparison
return nil, fmt.Errorf("unsupported type")
}
return
}
// Divide attempts to divide multiple items
// returns the result or error if not comparable
func Divide(items ...interface{}) (mlt interface{}, err error) {
//we need at least 2 items to diff them
if len(items) < 2 {
return nil, ErrNotEnoughParameters
}
switch dt := items[0].(type) {
case float64:
mlt = dt
for _, item := range items[1:] {
if itmVal, err := IfaceAsFloat64(item); err != nil {
return nil, err
} else {
mlt = mlt.(float64) / itmVal
}
}
case int64:
mlt = dt
for _, item := range items[1:] {
if itmVal, err := IfaceAsInt64(item); err != nil {
return nil, err
} else {
mlt = mlt.(int64) / itmVal
}
}
case int:
// need explicit conversion for int
if firstItmVal, err := IfaceAsInt64(dt); err != nil {
return nil, err
} else {
mlt = firstItmVal
}
for _, item := range items[1:] {
if itmVal, err := IfaceAsInt64(item); err != nil {
return nil, err
} else {
mlt = mlt.(int64) / itmVal
}
}
default: // unsupported comparison
return nil, fmt.Errorf("unsupported type")
}
return
}
// ReflectFieldMethodInterface parses intf attepting to return the field value or error otherwise
// Supports "ExtraFields" where additional fields are dynamically inserted in map with field name: extraFieldsLabel
func ReflectFieldMethodInterface(obj interface{}, fldName string) (retIf interface{}, err error) {

View File

@@ -641,6 +641,56 @@ func TestDifference(t *testing.T) {
}
func TestMultiply(t *testing.T) {
if _, err := Multiply(10); err == nil || err != ErrNotEnoughParameters {
t.Error(err)
}
if _, err := Multiply(10, 1.2, false); err == nil || err.Error() != "cannot convert field: 1.2 to int" {
t.Error(err)
}
if diff, err := Multiply(12, 1, 2, 3); err != nil {
t.Error(err)
} else if diff != int64(72) {
t.Errorf("Expecting: 72, received: %+v", diff)
}
if diff, err := Multiply(8.0, 4.0, 2.0, 1.0); err != nil {
t.Error(err)
} else if diff != 64.0 {
t.Errorf("Expecting: 64.0, received: %+v", diff)
}
if diff, err := Multiply(8.0, 4, 6.0, 1.0); err != nil {
t.Error(err)
} else if diff != 192.0 {
t.Errorf("Expecting: 192.0, received: %+v", diff)
}
}
func TestDivide(t *testing.T) {
if _, err := Divide(10); err == nil || err != ErrNotEnoughParameters {
t.Error(err)
}
if _, err := Divide(10, 1.2, false); err == nil || err.Error() != "cannot convert field: 1.2 to int" {
t.Error(err)
}
if diff, err := Divide(12, 1, 2, 3); err != nil {
t.Error(err)
} else if diff != int64(2) {
t.Errorf("Expecting: 2, received: %+v", diff)
}
if diff, err := Divide(8.0, 4.0, 2.0, 1.0); err != nil {
t.Error(err)
} else if diff != 1.0 {
t.Errorf("Expecting: 1.0, received: %+v", diff)
}
if diff, err := Divide(8.0, 4, 6.0, 1.0); err != nil {
t.Error(err)
} else if diff != 0.3333333333333333 {
t.Errorf("Expecting: 0.3333333333333333, received: %+v", diff)
}
}
func TestEqualTo(t *testing.T) {
if gte, err := EqualTo(1, 1.2); err != nil {
t.Error(err)