From 1db592ea8d552afdf82cd70feb5769d7fc9c6d58 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 5 May 2021 13:02:38 +0300 Subject: [PATCH] Merged template parsing functions --- agents/agentreq.go | 160 +------------------ engine/attributes.go | 339 ++++++++++++++++++++++------------------- engine/eventrequest.go | 167 ++------------------ utils/consts.go | 11 ++ utils/reflect.go | 95 +++++++----- utils/reflect_test.go | 30 ++-- 6 files changed, 283 insertions(+), 519 deletions(-) diff --git a/agents/agentreq.go b/agents/agentreq.go index e49c30372..d669d7f31 100644 --- a/agents/agentreq.go +++ b/agents/agentreq.go @@ -20,11 +20,8 @@ package agents import ( "fmt" - "math" "net" - "strconv" "strings" - "time" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/engine" @@ -321,164 +318,21 @@ func (ar *AgentRequest) Remove(fullPath *utils.FullPath) error { // ParseField outputs the value based on the template item func (ar *AgentRequest) ParseField( cfgFld *config.FCTemplate) (out interface{}, err error) { - var isString bool - switch cfgFld.Type { - default: - return utils.EmptyString, fmt.Errorf("unsupported type: <%s>", cfgFld.Type) - case utils.MetaNone: - return + tmpType := cfgFld.Type + switch tmpType { case utils.MetaFiller: - out, err = cfgFld.Value.ParseValue(utils.EmptyString) cfgFld.Padding = utils.MetaRight - isString = true - case utils.MetaConstant: - out, err = cfgFld.Value.ParseValue(utils.EmptyString) - isString = true - case utils.MetaRemoteHost: - out = ar.RemoteHost().String() - isString = true - case utils.MetaVariable, utils.MetaComposed, utils.MetaGroup: - out, err = cfgFld.Value.ParseDataProvider(ar) - isString = true - case utils.MetaUsageDifference: - if len(cfgFld.Value) != 2 { - return nil, fmt.Errorf("invalid arguments <%s> to %s", - utils.ToJSON(cfgFld.Value), utils.MetaUsageDifference) - } - var strVal1 string - if strVal1, err = cfgFld.Value[0].ParseDataProvider(ar); err != nil { - return - } - var strVal2 string - if strVal2, err = cfgFld.Value[1].ParseDataProvider(ar); err != nil { - return - } - var tEnd time.Time - if tEnd, err = utils.ParseTimeDetectLayout(strVal1, ar.Timezone); err != nil { - return - } - var tStart time.Time - if tStart, err = utils.ParseTimeDetectLayout(strVal2, ar.Timezone); err != nil { - return - } - out = tEnd.Sub(tStart).String() - isString = true - case utils.MetaCCUsage: - if len(cfgFld.Value) != 3 { - return nil, fmt.Errorf("invalid arguments <%s> to %s", - utils.ToJSON(cfgFld.Value), utils.MetaCCUsage) - } - var strVal1 string - if strVal1, err = cfgFld.Value[0].ParseDataProvider(ar); err != nil { - return - } - var reqNr int64 - if reqNr, err = strconv.ParseInt(strVal1, 10, 64); err != nil { - err = fmt.Errorf("invalid requestNumber <%s> to %s", - strVal1, utils.MetaCCUsage) - return - } - var strVal2 string - if strVal2, err = cfgFld.Value[1].ParseDataProvider(ar); err != nil { - return - } - var usedCCTime time.Duration - if usedCCTime, err = utils.ParseDurationWithNanosecs(strVal2); err != nil { - err = fmt.Errorf("invalid usedCCTime <%s> to %s", - strVal2, utils.MetaCCUsage) - return - } - var strVal3 string - if strVal3, err = cfgFld.Value[2].ParseDataProvider(ar); err != nil { - return - } - var debitItvl time.Duration - if debitItvl, err = utils.ParseDurationWithNanosecs(strVal3); err != nil { - err = fmt.Errorf("invalid debitInterval <%s> to %s", - strVal3, utils.MetaCCUsage) - return - } - if reqNr--; reqNr < 0 { // terminate will be ignored (init request should always be 0) - reqNr = 0 - } - return usedCCTime + time.Duration(debitItvl.Nanoseconds()*reqNr), nil - case utils.MetaSum: - var iFaceVals []interface{} - if iFaceVals, err = cfgFld.Value.GetIfaceFromValues(ar); err != nil { - return - } - out, err = utils.Sum(iFaceVals...) - case utils.MetaDifference: - var iFaceVals []interface{} - if iFaceVals, err = cfgFld.Value.GetIfaceFromValues(ar); err != nil { - return - } - out, err = utils.Difference(iFaceVals...) - case utils.MetaMultiply: - var iFaceVals []interface{} - if iFaceVals, err = cfgFld.Value.GetIfaceFromValues(ar); err != nil { - return - } - out, err = utils.Multiply(iFaceVals...) - case utils.MetaDivide: - var iFaceVals []interface{} - if iFaceVals, err = cfgFld.Value.GetIfaceFromValues(ar); err != nil { - return - } - out, err = utils.Divide(iFaceVals...) - case utils.MetaValueExponent: - if len(cfgFld.Value) != 2 { - return nil, fmt.Errorf("invalid arguments <%s> to %s", - utils.ToJSON(cfgFld.Value), utils.MetaValueExponent) - } - var strVal1 string - if strVal1, err = cfgFld.Value[0].ParseDataProvider(ar); err != nil { - return - } - var val float64 - if val, err = strconv.ParseFloat(strVal1, 64); err != nil { - err = fmt.Errorf("invalid value <%s> to %s", - strVal1, utils.MetaValueExponent) - return - } - var strVal2 string - if strVal2, err = cfgFld.Value[1].ParseDataProvider(ar); err != nil { - return - } - var exp int - if exp, err = strconv.Atoi(strVal2); err != nil { - return - } - out = strconv.FormatFloat(utils.Round(val*math.Pow10(exp), - config.CgrConfig().GeneralCfg().RoundingDecimals, utils.MetaRoundingMiddle), 'f', -1, 64) - case utils.MetaUnixTimestamp: - var val string - if val, err = cfgFld.Value.ParseDataProvider(ar); err != nil { - return - } - var t1 time.Time - if t1, err = utils.ParseTimeDetectLayout(val, cfgFld.Timezone); err != nil { - return - } - out = strconv.Itoa(int(t1.Unix())) - case utils.MetaDateTime: // Convert the requested field value into datetime with layout - var val string - if val, err = cfgFld.Value.ParseDataProvider(ar); err != nil { - return - } - var dtFld time.Time - dtFld, err = utils.ParseTimeDetectLayout(val, utils.FirstNonEmpty(cfgFld.Timezone, config.CgrConfig().GeneralCfg().DefaultTimezone)) - if err != nil { - return - } - out = dtFld.Format(cfgFld.Layout) + tmpType = utils.MetaConstant + case utils.MetaGroup: + tmpType = utils.MetaVariable } + out, err = engine.ParseAttribute(ar, tmpType, cfgFld.Path, cfgFld.Value, config.CgrConfig().GeneralCfg().RoundingDecimals, utils.FirstNonEmpty(cfgFld.Timezone, config.CgrConfig().GeneralCfg().DefaultTimezone), cfgFld.Layout, config.CgrConfig().GeneralCfg().RSRSep) if err != nil && !strings.HasPrefix(err.Error(), "Could not find") { return } - if isString { // format the string additionally with fmtFieldWidth + if utils.StringTmplType.Has(tmpType) { // format the string additionally with fmtFieldWidth out, err = utils.FmtFieldWidth(cfgFld.Tag, out.(string), cfgFld.Width, cfgFld.Strip, cfgFld.Padding, cfgFld.Mandatory) } diff --git a/engine/attributes.go b/engine/attributes.go index 78a90a36f..7de154d5c 100644 --- a/engine/attributes.go +++ b/engine/attributes.go @@ -219,168 +219,12 @@ func (alS *AttributeService) processEvent(tnt string, args *AttrArgsProcessEvent continue } } - var substitute string - switch attribute.Type { - case utils.MetaConstant: - substitute, err = attribute.Value.ParseValue(utils.EmptyString) - case utils.MetaVariable, utils.MetaComposed: - substitute, err = attribute.Value.ParseDataProvider(dynDP) - case utils.MetaUsageDifference: - if len(attribute.Value) != 2 { - return nil, fmt.Errorf("invalid arguments <%s>", utils.ToJSON(attribute.Value)) - } - var strVal1 string - if strVal1, err = attribute.Value[0].ParseDataProvider(dynDP); err != nil { - rply = nil - return - } - var strVal2 string - if strVal2, err = attribute.Value[1].ParseDataProvider(dynDP); err != nil { - rply = nil - return - } - var tEnd time.Time - if tEnd, err = utils.ParseTimeDetectLayout(strVal1, utils.EmptyString); err != nil { - rply = nil - return - } - var tStart time.Time - if tStart, err = utils.ParseTimeDetectLayout(strVal2, utils.EmptyString); err != nil { - rply = nil - return - } - substitute = tEnd.Sub(tStart).String() - case utils.MetaSum: - var ifaceVals []interface{} - if ifaceVals, err = attribute.Value.GetIfaceFromValues(dynDP); err != nil { - rply = nil - return - } - var ifaceSum interface{} - if ifaceSum, err = utils.Sum(ifaceVals...); err != nil { - rply = nil - return - } - substitute = utils.IfaceAsString(ifaceSum) - case utils.MetaDifference: - var ifaceVals []interface{} - if ifaceVals, err = attribute.Value.GetIfaceFromValues(dynDP); err != nil { - rply = nil - return - } - var ifaceSum interface{} - if ifaceSum, err = utils.Difference(ifaceVals...); err != nil { - rply = nil - return - } - substitute = utils.IfaceAsString(ifaceSum) - case utils.MetaMultiply: - var ifaceVals []interface{} - if ifaceVals, err = attribute.Value.GetIfaceFromValues(dynDP); err != nil { - rply = nil - return - } - var ifaceSum interface{} - if ifaceSum, err = utils.Multiply(ifaceVals...); err != nil { - rply = nil - return - } - substitute = utils.IfaceAsString(ifaceSum) - case utils.MetaDivide: - var ifaceVals []interface{} - if ifaceVals, err = attribute.Value.GetIfaceFromValues(dynDP); err != nil { - rply = nil - return - } - var ifaceSum interface{} - if ifaceSum, err = utils.Divide(ifaceVals...); err != nil { - rply = nil - return - } - substitute = utils.IfaceAsString(ifaceSum) - case utils.MetaValueExponent: - if len(attribute.Value) != 2 { - return nil, fmt.Errorf("invalid arguments <%s> to %s", - utils.ToJSON(attribute.Value), utils.MetaValueExponent) - } - var strVal1 string - if strVal1, err = attribute.Value[0].ParseDataProvider(dynDP); err != nil { - rply = nil - return - } - var val float64 - if val, err = strconv.ParseFloat(strVal1, 64); err != nil { - return nil, fmt.Errorf("invalid value <%s> to %s", - strVal1, utils.MetaValueExponent) - } - var strVal2 string - if strVal2, err = attribute.Value[1].ParseDataProvider(dynDP); err != nil { - rply = nil - return - } - var exp int - if exp, err = strconv.Atoi(strVal2); err != nil { - rply = nil - return - } - substitute = strconv.FormatFloat(utils.Round(val*math.Pow10(exp), - alS.cgrcfg.GeneralCfg().RoundingDecimals, utils.MetaRoundingMiddle), 'f', -1, 64) - case utils.MetaUnixTimestamp: - var val string - if val, err = attribute.Value.ParseDataProvider(dynDP); err != nil { - rply = nil - return - } - var t time.Time - if t, err = utils.ParseTimeDetectLayout(val, alS.cgrcfg.GeneralCfg().DefaultTimezone); err != nil { - rply = nil - return - } - substitute = strconv.Itoa(int(t.Unix())) - case utils.MetaPrefix: - var pathRsr config.RSRParsers - pathRsr, err = config.NewRSRParsers(utils.DynamicDataPrefix+attribute.Path, alS.cgrcfg.GeneralCfg().RSRSep) - if err != nil { - rply = nil - return - } - var pathVal string - if pathVal, err = pathRsr.ParseDataProvider(dynDP); err != nil { - rply = nil - return - } - var val string - if val, err = attribute.Value.ParseDataProvider(dynDP); err != nil { - rply = nil - return - } - substitute = val + pathVal - case utils.MetaSuffix: - var pathRsr config.RSRParsers - pathRsr, err = config.NewRSRParsers(utils.DynamicDataPrefix+attribute.Path, alS.cgrcfg.GeneralCfg().RSRSep) - if err != nil { - rply = nil - return - } - var pathVal string - if pathVal, err = pathRsr.ParseDataProvider(dynDP); err != nil { - rply = nil - return - } - var val string - if val, err = attribute.Value.ParseDataProvider(dynDP); err != nil { - rply = nil - return - } - substitute = pathVal + val - default: // backwards compatible in case that Type is empty - substitute, err = attribute.Value.ParseDataProvider(dynDP) - } - - if err != nil { + var out interface{} + if out, err = ParseAttribute(dynDP, utils.FirstNonEmpty(attribute.Type, utils.MetaVariable), utils.DynamicDataPrefix+attribute.Path, attribute.Value, alS.cgrcfg.GeneralCfg().RoundingDecimals, alS.cgrcfg.GeneralCfg().DefaultTimezone, time.RFC3339, alS.cgrcfg.GeneralCfg().RSRSep); err != nil { rply = nil return } + substitute := utils.IfaceAsString(out) //add only once the Path in AlteredFields if !utils.IsSliceMember(rply.AlteredFields, attribute.Path) { rply.AlteredFields = append(rply.AlteredFields, attribute.Path) @@ -517,3 +361,180 @@ func (alS *AttributeService) V1ProcessEvent(args *AttrArgsProcessEvent, } return } + +func ParseAttribute(dp utils.DataProvider, attrType, path string, value config.RSRParsers, roundingDec int, timeZone, layout, rsrSep string) ( + out interface{}, err error) { + switch attrType { + case utils.MetaNone: + return + case utils.MetaConstant: + out, err = value.ParseValue(utils.EmptyString) + case utils.MetaVariable, utils.MetaComposed: + out, err = value.ParseDataProvider(dp) + case utils.MetaUsageDifference: + if len(value) != 2 { + return "", fmt.Errorf("invalid arguments <%s> to %s", + utils.ToJSON(value), utils.MetaUsageDifference) + } + var strVal1 string + if strVal1, err = value[0].ParseDataProvider(dp); err != nil { + return + } + var strVal2 string + if strVal2, err = value[1].ParseDataProvider(dp); err != nil { + return + } + var tEnd time.Time + if tEnd, err = utils.ParseTimeDetectLayout(strVal1, utils.EmptyString); err != nil { + return + } + var tStart time.Time + if tStart, err = utils.ParseTimeDetectLayout(strVal2, utils.EmptyString); err != nil { + return + } + out = tEnd.Sub(tStart).String() + case utils.MetaSum: + var ifaceVals []interface{} + if ifaceVals, err = value.GetIfaceFromValues(dp); err != nil { + return + } + out, err = utils.Sum(ifaceVals...) + case utils.MetaDifference: + var ifaceVals []interface{} + if ifaceVals, err = value.GetIfaceFromValues(dp); err != nil { + return + } + out, err = utils.Difference(timeZone, ifaceVals...) + case utils.MetaMultiply: + var ifaceVals []interface{} + if ifaceVals, err = value.GetIfaceFromValues(dp); err != nil { + return + } + out, err = utils.Multiply(ifaceVals...) + case utils.MetaDivide: + var ifaceVals []interface{} + if ifaceVals, err = value.GetIfaceFromValues(dp); err != nil { + return + } + out, err = utils.Divide(ifaceVals...) + case utils.MetaValueExponent: + if len(value) != 2 { + return "", fmt.Errorf("invalid arguments <%s> to %s", + utils.ToJSON(value), utils.MetaValueExponent) + } + var strVal1 string + if strVal1, err = value[0].ParseDataProvider(dp); err != nil { + return + } + var val float64 + if val, err = strconv.ParseFloat(strVal1, 64); err != nil { + return "", fmt.Errorf("invalid value <%s> to %s", + strVal1, utils.MetaValueExponent) + } + var strVal2 string + if strVal2, err = value[1].ParseDataProvider(dp); err != nil { + return + } + var exp int + if exp, err = strconv.Atoi(strVal2); err != nil { + return + } + out = strconv.FormatFloat(utils.Round(val*math.Pow10(exp), + roundingDec, utils.MetaRoundingMiddle), 'f', -1, 64) + case utils.MetaUnixTimestamp: + var val string + if val, err = value.ParseDataProvider(dp); err != nil { + return + } + var t time.Time + if t, err = utils.ParseTimeDetectLayout(val, timeZone); err != nil { + return + } + out = strconv.Itoa(int(t.Unix())) + case utils.MetaDateTime: // Convert the requested field value into datetime with layout + var val string + if val, err = value.ParseDataProvider(dp); err != nil { + return + } + var dtFld time.Time + dtFld, err = utils.ParseTimeDetectLayout(val, timeZone) + if err != nil { + return + } + out = dtFld.Format(layout) + case utils.MetaPrefix: + var pathRsr config.RSRParsers + pathRsr, err = config.NewRSRParsers(path, rsrSep) + if err != nil { + return + } + var pathVal string + if pathVal, err = pathRsr.ParseDataProvider(dp); err != nil { + return + } + var val string + if val, err = value.ParseDataProvider(dp); err != nil { + return + } + out = val + pathVal + case utils.MetaSuffix: + var pathRsr config.RSRParsers + pathRsr, err = config.NewRSRParsers(path, rsrSep) + if err != nil { + return + } + var pathVal string + if pathVal, err = pathRsr.ParseDataProvider(dp); err != nil { + return + } + var val string + if val, err = value.ParseDataProvider(dp); err != nil { + return + } + out = pathVal + val + case utils.MetaRemoteHost: + out = dp.RemoteHost().String() + case utils.MetaCCUsage: + if len(value) != 3 { + return nil, fmt.Errorf("invalid arguments <%s> to %s", + utils.ToJSON(value), utils.MetaCCUsage) + } + var strVal1 string + if strVal1, err = value[0].ParseDataProvider(dp); err != nil { + return + } + var reqNr int64 + if reqNr, err = strconv.ParseInt(strVal1, 10, 64); err != nil { + err = fmt.Errorf("invalid requestNumber <%s> to %s", + strVal1, utils.MetaCCUsage) + return + } + var strVal2 string + if strVal2, err = value[1].ParseDataProvider(dp); err != nil { + return + } + var usedCCTime time.Duration + if usedCCTime, err = utils.ParseDurationWithNanosecs(strVal2); err != nil { + err = fmt.Errorf("invalid usedCCTime <%s> to %s", + strVal2, utils.MetaCCUsage) + return + } + var strVal3 string + if strVal3, err = value[2].ParseDataProvider(dp); err != nil { + return + } + var debitItvl time.Duration + if debitItvl, err = utils.ParseDurationWithNanosecs(strVal3); err != nil { + err = fmt.Errorf("invalid debitInterval <%s> to %s", + strVal3, utils.MetaCCUsage) + return + } + if reqNr--; reqNr < 0 { // terminate will be ignored (init request should always be 0) + reqNr = 0 + } + return usedCCTime + time.Duration(debitItvl.Nanoseconds()*reqNr), nil + default: + return utils.EmptyString, fmt.Errorf("unsupported type: <%s>", attrType) + } + return +} diff --git a/engine/eventrequest.go b/engine/eventrequest.go index 0ba51b367..1466b1923 100644 --- a/engine/eventrequest.go +++ b/engine/eventrequest.go @@ -20,11 +20,8 @@ package engine import ( "fmt" - "math" "net" - "strconv" "strings" - "time" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" @@ -191,158 +188,8 @@ func (eeR *EventRequest) SetAsSlice(fullPath *utils.FullPath, val *utils.DataLea // ParseField outputs the value based on the template item func (eeR *EventRequest) ParseField( cfgFld *config.FCTemplate) (out interface{}, err error) { - var isString bool - switch cfgFld.Type { - default: - return utils.EmptyString, fmt.Errorf("unsupported type: <%s>", cfgFld.Type) - case utils.MetaNone: - return - case utils.MetaFiller: - out, err = cfgFld.Value.ParseValue(utils.EmptyString) - cfgFld.Padding = utils.MetaRight - isString = true - case utils.MetaConstant: - out, err = cfgFld.Value.ParseValue(utils.EmptyString) - isString = true - case utils.MetaRemoteHost: - out = eeR.RemoteHost().String() - isString = true - case utils.MetaVariable, utils.MetaComposed, utils.MetaGroup: - out, err = cfgFld.Value.ParseDataProvider(eeR) - isString = true - case utils.MetaUsageDifference: - if len(cfgFld.Value) != 2 { - return nil, fmt.Errorf("invalid arguments <%s> to %s", - utils.ToJSON(cfgFld.Value), utils.MetaUsageDifference) - } - var strVal1 string - if strVal1, err = cfgFld.Value[0].ParseDataProvider(eeR); err != nil { - return - } - var strVal2 string - if strVal2, err = cfgFld.Value[1].ParseDataProvider(eeR); err != nil { - return - } - var tEnd time.Time - if tEnd, err = utils.ParseTimeDetectLayout(strVal1, eeR.Timezone); err != nil { - return - } - var tStart time.Time - if tStart, err = utils.ParseTimeDetectLayout(strVal2, eeR.Timezone); err != nil { - return - } - out = tEnd.Sub(tStart).String() - isString = true - case utils.MetaCCUsage: - if len(cfgFld.Value) != 3 { - return nil, fmt.Errorf("invalid arguments <%s> to %s", - utils.ToJSON(cfgFld.Value), utils.MetaCCUsage) - } - var strVal1 string - if strVal1, err = cfgFld.Value[0].ParseDataProvider(eeR); err != nil { - return - } - var reqNr int64 - if reqNr, err = strconv.ParseInt(strVal1, 10, 64); err != nil { - err = fmt.Errorf("invalid requestNumber <%s> to %s", - strVal1, utils.MetaCCUsage) - return - } - var strVal2 string - if strVal2, err = cfgFld.Value[1].ParseDataProvider(eeR); err != nil { - return - } - var usedCCTime time.Duration - if usedCCTime, err = utils.ParseDurationWithNanosecs(strVal2); err != nil { - err = fmt.Errorf("invalid usedCCTime <%s> to %s", - strVal2, utils.MetaCCUsage) - return - } - var strVal3 string - if strVal3, err = cfgFld.Value[2].ParseDataProvider(eeR); err != nil { - return - } - var debitItvl time.Duration - if debitItvl, err = utils.ParseDurationWithNanosecs(strVal3); err != nil { - err = fmt.Errorf("invalid debitInterval <%s> to %s", - strVal3, utils.MetaCCUsage) - return - } - - if reqNr--; reqNr < 0 { // terminate will be ignored (init request should always be 0) - reqNr = 0 - } - return usedCCTime + time.Duration(debitItvl.Nanoseconds()*reqNr), nil - case utils.MetaSum: - var iFaceVals []interface{} - if iFaceVals, err = cfgFld.Value.GetIfaceFromValues(eeR); err != nil { - return - } - out, err = utils.Sum(iFaceVals...) - case utils.MetaDifference: - var iFaceVals []interface{} - if iFaceVals, err = cfgFld.Value.GetIfaceFromValues(eeR); err != nil { - return - } - out, err = utils.Difference(iFaceVals...) - case utils.MetaMultiply: - var iFaceVals []interface{} - if iFaceVals, err = cfgFld.Value.GetIfaceFromValues(eeR); err != nil { - return - } - out, err = utils.Multiply(iFaceVals...) - case utils.MetaDivide: - var iFaceVals []interface{} - if iFaceVals, err = cfgFld.Value.GetIfaceFromValues(eeR); err != nil { - return - } - out, err = utils.Divide(iFaceVals...) - case utils.MetaValueExponent: - if len(cfgFld.Value) != 2 { - return nil, fmt.Errorf("invalid arguments <%s> to %s", - utils.ToJSON(cfgFld.Value), utils.MetaValueExponent) - } - var strVal1 string - if strVal1, err = cfgFld.Value[0].ParseDataProvider(eeR); err != nil { - return - } - var val float64 - if val, err = strconv.ParseFloat(strVal1, 64); err != nil { - err = fmt.Errorf("invalid value <%s> to %s", - strVal1, utils.MetaValueExponent) - return - } - var strVal2 string - if strVal2, err = cfgFld.Value[1].ParseDataProvider(eeR); err != nil { - return - } - var exp int - if exp, err = strconv.Atoi(strVal2); err != nil { - return - } - out = strconv.FormatFloat(utils.Round(val*math.Pow10(exp), - config.CgrConfig().GeneralCfg().RoundingDecimals, utils.MetaRoundingMiddle), 'f', -1, 64) - case utils.MetaUnixTimestamp: - var val string - if val, err = cfgFld.Value.ParseDataProvider(eeR); err != nil { - return - } - var t1 time.Time - if t1, err = utils.ParseTimeDetectLayout(val, cfgFld.Timezone); err != nil { - return - } - out = strconv.Itoa(int(t1.Unix())) - case utils.MetaDateTime: // Convert the requested field value into datetime with layout - var val string - if val, err = cfgFld.Value.ParseDataProvider(eeR); err != nil { - return - } - var dtFld time.Time - dtFld, err = utils.ParseTimeDetectLayout(val, utils.FirstNonEmpty(cfgFld.Timezone, config.CgrConfig().GeneralCfg().DefaultTimezone)) - if err != nil { - return - } - out = dtFld.Format(cfgFld.Layout) + tmpType := cfgFld.Type + switch tmpType { case utils.MetaMaskedDestination: //check if we have destination in the event var dst string @@ -355,14 +202,20 @@ func (eeR *EventRequest) ParseField( CachedDestHasPrefix(cfgFld.MaskDestID, dst) { out = utils.MaskSuffix(dst, cfgFld.MaskLen) } - + return + case utils.MetaFiller: + cfgFld.Padding = utils.MetaRight + tmpType = utils.MetaConstant + case utils.MetaGroup: + tmpType = utils.MetaVariable } + out, err = ParseAttribute(eeR, tmpType, cfgFld.Path, cfgFld.Value, config.CgrConfig().GeneralCfg().RoundingDecimals, utils.FirstNonEmpty(cfgFld.Timezone, config.CgrConfig().GeneralCfg().DefaultTimezone), cfgFld.Layout, config.CgrConfig().GeneralCfg().RSRSep) if err != nil && !strings.HasPrefix(err.Error(), "Could not find") { return } - if isString { // format the string additionally with fmtFieldWidth + if utils.StringTmplType.Has(tmpType) { // format the string additionally with fmtFieldWidth out, err = utils.FmtFieldWidth(cfgFld.Tag, out.(string), cfgFld.Width, cfgFld.Strip, cfgFld.Padding, cfgFld.Mandatory) } diff --git a/utils/consts.go b/utils/consts.go index 7dc3204fa..5638402c7 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -2660,6 +2660,17 @@ var ( } ) +// StringTmplType a string set used, by agentRequest and eventRequest to determine if the returned template type is string +var StringTmplType = StringSet{ + MetaConstant: struct{}{}, + MetaRemoteHost: struct{}{}, + MetaVariable: struct{}{}, + MetaComposed: struct{}{}, + MetaUsageDifference: struct{}{}, + MetaPrefix: struct{}{}, + MetaSuffix: struct{}{}, +} + // Time duration suffix const ( NsSuffix = "ns" diff --git a/utils/reflect.go b/utils/reflect.go index 7fa738000..1215f6a78 100644 --- a/utils/reflect.go +++ b/utils/reflect.go @@ -550,107 +550,126 @@ func Sum(items ...interface{}) (sum interface{}, err error) { switch dt := items[0].(type) { case time.Duration: - sum = dt + s := dt for _, item := range items[1:] { if itmVal, err := IfaceAsDuration(item); err != nil { return nil, err } else { - sum = sum.(time.Duration) + itmVal + s += itmVal } } + sum = s case time.Time: - sum = dt + s := dt for _, item := range items[1:] { if itmVal, err := IfaceAsDuration(item); err != nil { return nil, err } else { - sum = sum.(time.Time).Add(itmVal) + s = s.Add(itmVal) } } + sum = s case float64: - sum = dt + s := dt for _, item := range items[1:] { if itmVal, err := IfaceAsFloat64(item); err != nil { return nil, err } else { - sum = sum.(float64) + itmVal + s += itmVal } } + sum = s case int64: - sum = dt + s := dt for _, item := range items[1:] { if itmVal, err := IfaceAsInt64(item); err != nil { return nil, err } else { - sum = sum.(int64) + itmVal + s += itmVal } } + sum = s case int: - sum = int64(dt) + s := int64(dt) for _, item := range items[1:] { if itmVal, err := IfaceAsInt64(item); err != nil { return nil, err } else { - sum = sum.(int64) + itmVal + s += itmVal } } + sum = s } return } // Difference attempts to sum multiple items // returns the result or error if not comparable -func Difference(items ...interface{}) (diff interface{}, err error) { +func Difference(tm string, items ...interface{}) (diff 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 time.Duration: - diff = dt + d := dt for _, item := range items[1:] { if itmVal, err := IfaceAsDuration(item); err != nil { return nil, err } else { - diff = diff.(time.Duration) - itmVal + d -= itmVal } } + diff = d case time.Time: - diff = dt - for _, item := range items[1:] { + d := dt + for i, item := range items[1:] { + if itmVal, err := IfaceAsTime(item, tm); err == nil { + diff = d.Sub(itmVal) + if len(items) == i+1 { + return diff, nil + } + items[i] = diff + return Difference(tm, items[i:]...) + } + if itmVal, err := IfaceAsDuration(item); err != nil { return nil, err } else { - diff = diff.(time.Time).Add(-itmVal) + d = d.Add(-itmVal) } } + diff = d case float64: - diff = dt + d := dt for _, item := range items[1:] { if itmVal, err := IfaceAsFloat64(item); err != nil { return nil, err } else { - diff = diff.(float64) - itmVal + d -= itmVal } } + diff = d case int64: - diff = dt + d := dt for _, item := range items[1:] { if itmVal, err := IfaceAsInt64(item); err != nil { return nil, err } else { - diff = diff.(int64) - itmVal + d -= itmVal } } + diff = d case int: - diff = int64(dt) + d := int64(dt) for _, item := range items[1:] { if itmVal, err := IfaceAsInt64(item); err != nil { return nil, err } else { - diff = diff.(int64) - itmVal + d -= itmVal } } + diff = d default: // unsupported comparison return nil, fmt.Errorf("unsupported type") } @@ -666,32 +685,35 @@ func Multiply(items ...interface{}) (mlt interface{}, err error) { } switch dt := items[0].(type) { case float64: - mlt = dt + m := dt for _, item := range items[1:] { if itmVal, err := IfaceAsFloat64(item); err != nil { return nil, err } else { - mlt = mlt.(float64) * itmVal + m *= itmVal } } + mlt = m case int64: - mlt = dt + m := dt for _, item := range items[1:] { if itmVal, err := IfaceAsInt64(item); err != nil { return nil, err } else { - mlt = mlt.(int64) * itmVal + m *= itmVal } } + mlt = m case int: - mlt = int64(dt) + m := int64(dt) for _, item := range items[1:] { if itmVal, err := IfaceAsInt64(item); err != nil { return nil, err } else { - mlt = mlt.(int64) * itmVal + m *= itmVal } } + mlt = m default: // unsupported comparison return nil, fmt.Errorf("unsupported type") } @@ -700,39 +722,42 @@ func Multiply(items ...interface{}) (mlt interface{}, err error) { // Divide attempts to divide multiple items // returns the result or error if not comparable -func Divide(items ...interface{}) (mlt interface{}, err error) { +func Divide(items ...interface{}) (div 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 + d := dt for _, item := range items[1:] { if itmVal, err := IfaceAsFloat64(item); err != nil { return nil, err } else { - mlt = mlt.(float64) / itmVal + d /= itmVal } } + div = d case int64: - mlt = dt + d := dt for _, item := range items[1:] { if itmVal, err := IfaceAsInt64(item); err != nil { return nil, err } else { - mlt = mlt.(int64) / itmVal + d /= itmVal } } + div = d case int: - mlt = int64(dt) + d := int64(dt) for _, item := range items[1:] { if itmVal, err := IfaceAsInt64(item); err != nil { return nil, err } else { - mlt = mlt.(int64) / itmVal + d /= itmVal } } + div = d default: // unsupported comparison return nil, fmt.Errorf("unsupported type") } diff --git a/utils/reflect_test.go b/utils/reflect_test.go index 1ed7fe1fd..8b13d2970 100644 --- a/utils/reflect_test.go +++ b/utils/reflect_test.go @@ -573,49 +573,49 @@ func TestGetUniformType(t *testing.T) { } func TestDifference(t *testing.T) { - if _, err := Difference(10); err == nil || err != ErrNotEnoughParameters { + if _, err := Difference("", 10); err == nil || err != ErrNotEnoughParameters { t.Error(err) } - if _, err := Difference(10, 1.2, false); err == nil || err.Error() != "cannot convert field: 1.2 to int" { + if _, err := Difference("", 10, 1.2, false); err == nil || err.Error() != "cannot convert field: 1.2 to int" { t.Error(err) } - if diff, err := Difference(12, 1, 2, 3); err != nil { + if diff, err := Difference("", 12, 1, 2, 3); err != nil { t.Error(err) } else if diff != int64(6) { t.Errorf("Expecting: 6, received: %+v", diff) } - if diff, err := Difference(8.0, 4.0, 2.0, -1.0); err != nil { + if diff, err := Difference("", 8.0, 4.0, 2.0, -1.0); err != nil { t.Error(err) } else if diff != 3.0 { t.Errorf("Expecting: 3.0, received: %+v", diff) } - if diff, err := Difference(8.0, 4, 2.0, -1.0); err != nil { + if diff, err := Difference("", 8.0, 4, 2.0, -1.0); err != nil { t.Error(err) } else if diff != 3.0 { t.Errorf("Expecting: 3.0, received: %+v", diff) } - if diff, err := Difference(10*time.Second, time.Second, 2*time.Second, + if diff, err := Difference("", 10*time.Second, time.Second, 2*time.Second, 4*time.Millisecond); err != nil { t.Error(err) } else if diff != 6*time.Second+996*time.Millisecond { t.Errorf("Expecting: 6.996ms, received: %+v", diff) } - if diff, err := Difference(2*time.Second, + if diff, err := Difference("", 2*time.Second, 10*time.Millisecond); err != nil { t.Error(err) } else if diff != time.Second+990*time.Millisecond { t.Errorf("Expecting: 1.99s, received: %+v", diff) } - if diff, err := Difference(time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), + if diff, err := Difference("", time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), 10*time.Second); err != nil { t.Error(err) } else if diff != time.Date(2009, 11, 10, 22, 59, 50, 0, time.UTC) { t.Errorf("Expecting: %+v, received: %+v", time.Date(2009, 11, 10, 22, 59, 50, 0, time.UTC), diff) } - if diff, err := Difference(time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), + if diff, err := Difference("", time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), 10*time.Second, 10000000000); err != nil { t.Error(err) } else if diff != time.Date(2009, 11, 10, 22, 59, 40, 0, time.UTC) { @@ -1405,7 +1405,7 @@ func TestReflectDifferenceTimeDurationError(t *testing.T) { var test2Var bool testVar = 25354 test2Var = true - _, err := Difference(testVar, test2Var) + _, err := Difference("", testVar, test2Var) if err == nil || err.Error() != "cannot convert field: true to time.Duration" { t.Errorf("Expected ,received: <%+v>", err) } @@ -1416,7 +1416,7 @@ func TestReflectDifferenceFloat64Error(t *testing.T) { var test2Var bool testVar = 2.5 test2Var = true - _, err := Difference(testVar, test2Var) + _, err := Difference("", testVar, test2Var) if err == nil || err.Error() != "cannot convert field: true to float64" { t.Errorf("Expected ,received: <%+v>", err) } @@ -1429,7 +1429,7 @@ func TestReflectDifferenceInt64Error(t *testing.T) { testVar = 6 test2Var = 5 expected = 1 - dif, _ := Difference(testVar, test2Var) + dif, _ := Difference("", testVar, test2Var) if !reflect.DeepEqual(dif, expected) { t.Errorf("Expected <%+v> ,received: <%+v>", expected, dif) } @@ -1440,7 +1440,7 @@ func TestReflectDifferenceDefault(t *testing.T) { var test2Var bool testVar = true test2Var = true - _, err := Difference(testVar, test2Var) + _, err := Difference("", testVar, test2Var) if err == nil || err.Error() != "unsupported type" { t.Errorf("Expected ,received: <%+v>", err) } @@ -1615,14 +1615,14 @@ func TestSumTimeTime(t *testing.T) { } func TestDifferenceTimeTimeError(t *testing.T) { - _, err := Difference(time.Now(), "cat") + _, err := Difference("", time.Now(), "cat") if err == nil || err.Error() != "time: invalid duration \"cat\"" { t.Errorf("Expected ,received: <%+v>", err) } } func TestDifferenceInt64Error(t *testing.T) { - _, err := Difference(int64(2), "cat") + _, err := Difference("", int64(2), "cat") if err == nil || err.Error() != "strconv.ParseInt: parsing \"cat\": invalid syntax" { t.Errorf("Expected ,received: <%+v>", err) }