mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-12 18:46:24 +05:00
209 lines
6.1 KiB
Go
209 lines
6.1 KiB
Go
/*
|
|
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 agents
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/cgrates/cgrates/config"
|
|
"github.com/cgrates/cgrates/engine"
|
|
"github.com/cgrates/cgrates/utils"
|
|
)
|
|
|
|
func newAgentRequest(req engine.DataProvider, tntTpl utils.RSRFields,
|
|
dfltTenant string, filterS *engine.FilterS) (ar *AgentRequest) {
|
|
ar = &AgentRequest{
|
|
Request: req,
|
|
Vars: engine.NewNavigableMap(nil),
|
|
CGRRequest: engine.NewNavigableMap(nil),
|
|
CGRReply: engine.NewNavigableMap(nil),
|
|
Reply: engine.NewNavigableMap(nil),
|
|
filterS: filterS,
|
|
}
|
|
// populate tenant
|
|
if tntIf, err := ar.ParseField(
|
|
&config.CfgCdrField{Type: utils.META_COMPOSED,
|
|
Value: tntTpl}); err == nil && tntIf.(string) != "" {
|
|
ar.Tenant = tntIf.(string)
|
|
} else {
|
|
ar.Tenant = dfltTenant
|
|
}
|
|
return
|
|
}
|
|
|
|
// AgentRequest represents data related to one request towards agent
|
|
// implements engine.DataProvider so we can pass it to filters
|
|
type AgentRequest struct {
|
|
Tenant string
|
|
Request engine.DataProvider // request
|
|
Vars *engine.NavigableMap // shared data
|
|
CGRRequest *engine.NavigableMap
|
|
CGRReply *engine.NavigableMap
|
|
Reply *engine.NavigableMap
|
|
filterS *engine.FilterS
|
|
}
|
|
|
|
// String implements engine.DataProvider
|
|
func (ar *AgentRequest) String() string {
|
|
return utils.ToJSON(ar)
|
|
}
|
|
|
|
// FieldAsInterface implements engine.DataProvider
|
|
func (ar *AgentRequest) FieldAsInterface(fldPath []string) (val interface{}, err error) {
|
|
switch fldPath[0] {
|
|
default:
|
|
return nil, fmt.Errorf("unsupported field prefix: <%s>", fldPath[0])
|
|
case utils.MetaReq:
|
|
return ar.Request.FieldAsInterface(fldPath[1:])
|
|
case utils.MetaVars:
|
|
return ar.Vars.FieldAsInterface(fldPath[1:])
|
|
case utils.MetaCgreq:
|
|
return ar.CGRRequest.FieldAsInterface(fldPath[1:])
|
|
case utils.MetaCgrep:
|
|
return ar.CGRReply.FieldAsInterface(fldPath[1:])
|
|
case utils.MetaRep:
|
|
return ar.Reply.FieldAsInterface(fldPath[1:])
|
|
}
|
|
}
|
|
|
|
// FieldAsString implements engine.DataProvider
|
|
func (ar *AgentRequest) FieldAsString(fldPath []string) (val string, err error) {
|
|
switch fldPath[0] {
|
|
default:
|
|
return "", fmt.Errorf("unsupported field prefix: <%s>", fldPath[0])
|
|
case utils.MetaReq:
|
|
return ar.Request.FieldAsString(fldPath[1:])
|
|
case utils.MetaVars:
|
|
return ar.Vars.FieldAsString(fldPath[1:])
|
|
case utils.MetaCgreq:
|
|
return ar.CGRRequest.FieldAsString(fldPath[1:])
|
|
case utils.MetaCgrep:
|
|
return ar.CGRReply.FieldAsString(fldPath[1:])
|
|
case utils.MetaRep:
|
|
return ar.Reply.FieldAsString(fldPath[1:])
|
|
}
|
|
}
|
|
|
|
// AsNavigableMap implements engine.DataProvider
|
|
func (ar *AgentRequest) AsNavigableMap(tplFlds []*config.CfgCdrField) (
|
|
nM *engine.NavigableMap, err error) {
|
|
nM = engine.NewNavigableMap(nil)
|
|
for _, tplFld := range tplFlds {
|
|
if pass, err := ar.filterS.Pass(ar.Tenant,
|
|
tplFld.Filters, ar); err != nil {
|
|
return nil, err
|
|
} else if !pass {
|
|
continue
|
|
}
|
|
out, err := ar.ParseField(tplFld)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var valSet []*engine.NMItem
|
|
fldPath := strings.Split(tplFld.FieldId, utils.NestingSep)
|
|
if nMFields, err := nM.FieldAsInterface(fldPath); err != nil {
|
|
if err != utils.ErrNotFound {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
valSet = nMFields.([]*engine.NMItem) // start from previous stored fields
|
|
}
|
|
valSet = append(valSet, &engine.NMItem{Data: out, Path: fldPath, Config: tplFld})
|
|
nM.Set(fldPath, valSet, true)
|
|
if tplFld.Blocker { // useful in case of processing errors first
|
|
break
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// parseField outputs the value based on the template item
|
|
func (aReq *AgentRequest) ParseField(
|
|
cfgFld *config.CfgCdrField) (out interface{}, err error) {
|
|
var isString bool
|
|
switch cfgFld.Type {
|
|
default:
|
|
return "", fmt.Errorf("unsupported type: <%s>", cfgFld.Type)
|
|
case utils.META_FILLER:
|
|
out = cfgFld.Value.Id()
|
|
cfgFld.Padding = "right"
|
|
isString = true
|
|
case utils.META_CONSTANT:
|
|
out = cfgFld.Value.Id()
|
|
isString = true
|
|
case utils.META_COMPOSED:
|
|
out = aReq.composedField(cfgFld.Value)
|
|
isString = true
|
|
case utils.META_USAGE_DIFFERENCE:
|
|
if len(cfgFld.Value) != 2 {
|
|
return nil, fmt.Errorf("invalid arguments <%s>", utils.ToJSON(cfgFld.Value))
|
|
} else {
|
|
strVal1, err := aReq.FieldAsString(strings.Split(cfgFld.Value[0].Id, utils.NestingSep))
|
|
strVal2, err := aReq.FieldAsString(strings.Split(cfgFld.Value[1].Id, utils.NestingSep))
|
|
tEnd, err := utils.ParseTimeDetectLayout(strVal1, cfgFld.Timezone)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
tStart, err := utils.ParseTimeDetectLayout(strVal2, cfgFld.Timezone)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
out = tEnd.Sub(tStart).String()
|
|
}
|
|
isString = true
|
|
}
|
|
if isString { // format the string additionally with fmtFieldWidth
|
|
out, err = utils.FmtFieldWidth(cfgFld.Tag, out.(string), cfgFld.Width,
|
|
cfgFld.Strip, cfgFld.Padding, cfgFld.Mandatory)
|
|
}
|
|
return
|
|
}
|
|
|
|
// composedField is a subset of ParseField
|
|
func (ar *AgentRequest) composedField(outTpl utils.RSRFields) (outVal string) {
|
|
for _, rsrTpl := range outTpl {
|
|
if rsrTpl.IsStatic() {
|
|
if parsed, err := rsrTpl.Parse(""); err != nil {
|
|
utils.Logger.Warning(
|
|
fmt.Sprintf("<%s> %s",
|
|
utils.HTTPAgent, err.Error()))
|
|
} else {
|
|
outVal += parsed
|
|
}
|
|
continue
|
|
}
|
|
valStr, err := ar.FieldAsString(strings.Split(rsrTpl.Id, utils.NestingSep))
|
|
if err != nil {
|
|
utils.Logger.Warning(
|
|
fmt.Sprintf("<%s> %s",
|
|
utils.HTTPAgent, err.Error()))
|
|
continue
|
|
}
|
|
if parsed, err := rsrTpl.Parse(valStr); err != nil {
|
|
utils.Logger.Warning(
|
|
fmt.Sprintf("<%s> %s",
|
|
utils.HTTPAgent, err.Error()))
|
|
} else {
|
|
outVal += parsed
|
|
}
|
|
}
|
|
return outVal
|
|
}
|