added RequestProcessors in fsAgent

This commit is contained in:
gezimbll
2025-02-27 17:00:29 +01:00
committed by Dan Christian Bogos
parent 0497016ad6
commit 1f686fca63
10 changed files with 366 additions and 57 deletions

View File

@@ -33,35 +33,55 @@ import (
"github.com/cgrates/fsock"
)
func NewFSsessions(fsAgentConfig *config.FsAgentCfg,
timezone string, connMgr *engine.ConnManager) (fsa *FSsessions) {
fsa = &FSsessions{
func NewFSsessions(cgrcfg *config.CGRConfig, filterS *engine.FilterS,
timezone string, connMgr *engine.ConnManager) (*FSsessions, error) {
fsAgentConfig := cgrcfg.FsAgentCfg()
fsa := &FSsessions{
cgrcfg: cgrcfg,
cfg: fsAgentConfig,
conns: make([]*fsock.FSock, len(fsAgentConfig.EventSocketConns)),
senderPools: make([]*fsock.FSockPool, len(fsAgentConfig.EventSocketConns)),
timezone: timezone,
connMgr: connMgr,
filterS: filterS,
}
srv, _ := birpc.NewService(fsa, "", false)
fsa.ctx = context.WithClient(context.TODO(), srv)
return
msgTemplates := fsa.cgrcfg.TemplatesCfg()
// Inflate *template field types
for _, procsr := range fsa.cfg.RequestProcessors {
if tpls, err := config.InflateTemplates(procsr.RequestFields, msgTemplates); err != nil {
return nil, err
} else if tpls != nil {
procsr.RequestFields = tpls
}
if tpls, err := config.InflateTemplates(procsr.ReplyFields, msgTemplates); err != nil {
return nil, err
} else if tpls != nil {
procsr.ReplyFields = tpls
}
}
return fsa, nil
}
// FSsessions is the freeswitch session manager
// it holds a buffer for the network connection
// and the active sessions
type FSsessions struct {
cgrcfg *config.CGRConfig
cfg *config.FsAgentCfg
conns []*fsock.FSock // Keep the list here for connection management purposes
senderPools []*fsock.FSockPool // Keep sender pools here
timezone string
connMgr *engine.ConnManager
ctx *context.Context
filterS *engine.FilterS
}
func (fsa *FSsessions) createHandlers() map[string][]func(string, int) {
ca := func(body string, connIdx int) {
fsa.onChannelAnswer(
NewFSEvent(body), connIdx)
}
ch := func(body string, connIdx int) {
@@ -140,16 +160,24 @@ func (fsa *FSsessions) unparkCall(uuid string, connIdx int, callDestNb, notify s
}
func (fsa *FSsessions) onChannelPark(fsev FSEvent, connIdx int) {
if fsev.GetReqType(utils.MetaDefault) == utils.MetaNone { // Not for us
return
}
if connIdx >= len(fsa.conns) { // protection against index out of range panic
err := fmt.Errorf("Index out of range[0,%v): %v ", len(fsa.conns), connIdx)
utils.Logger.Err(fmt.Sprintf("<%s> %s", utils.FreeSWITCHAgent, err.Error()))
return
}
fsev[VarCGROriginHost] = utils.FirstNonEmpty(fsev[VarCGROriginHost], fsa.cfg.EventSocketConns[connIdx].Alias) // rewrite the OriginHost variable if it is empty
authArgs := fsev.AsCGREvent(fsa.timezone)
authArgs, err := fsa.processRequest(fsev)
if err != nil {
utils.Logger.Err(fmt.Sprintf("<%s> error: %s", utils.FreeSWITCHAgent, err.Error()))
return
}
reqType, err := authArgs.FieldAsString("RequestType")
if err != nil {
utils.Logger.Err(fmt.Sprintf("<%s> failed to retrieve RequestType: %v", utils.FreeSWITCHAgent, err))
return
}
if reqType == utils.MetaNone {
return // do not process this request
}
authArgs.Event[FsConnID] = connIdx // Attach the connection ID
var authReply sessions.V1AuthorizeReply
if err := fsa.connMgr.Call(fsa.ctx, fsa.cfg.SessionSConns, utils.SessionSv1AuthorizeEvent, authArgs, &authReply); err != nil {
@@ -216,33 +244,43 @@ func (fsa *FSsessions) onChannelPark(fsev FSEvent, connIdx int) {
}
func (fsa *FSsessions) onChannelAnswer(fsev FSEvent, connIdx int) {
if fsev.GetReqType(utils.MetaDefault) == utils.MetaNone { // Do not process this request
return
}
if connIdx >= len(fsa.conns) { // protection against index out of range panic
err := fmt.Errorf("Index out of range[0,%v): %v ", len(fsa.conns), connIdx)
utils.Logger.Err(fmt.Sprintf("<%s> %s", utils.FreeSWITCHAgent, err.Error()))
return
}
_, err := fsa.conns[connIdx].SendApiCmd(
cgrEv, err := fsa.processRequest(fsev)
if err != nil {
utils.Logger.Err(fmt.Sprintf("<%s> error: %s", utils.FreeSWITCHAgent, err.Error()))
return
}
reqType, err := cgrEv.FieldAsString("RequestType")
if err != nil {
utils.Logger.Err(fmt.Sprintf("<%s> failed to retrieve RequestType: %v", utils.FreeSWITCHAgent, err))
return
}
if reqType == utils.MetaNone {
return // do not process this request
}
if _, err := fsa.conns[connIdx].SendApiCmd(
fmt.Sprintf("uuid_setvar %s %s %s\n\n", fsev.GetUUID(),
utils.CGROriginHost, utils.FirstNonEmpty(fsa.cfg.EventSocketConns[connIdx].Alias,
fsa.cfg.EventSocketConns[connIdx].Address)))
if err != nil {
fsa.cfg.EventSocketConns[connIdx].Address))); err != nil {
utils.Logger.Err(
fmt.Sprintf("<%s> error %s setting channel variabile: %s",
utils.FreeSWITCHAgent, err.Error(), VarCGROriginHost))
return
}
fsev[VarCGROriginHost] = utils.FirstNonEmpty(fsev[VarCGROriginHost], fsa.cfg.EventSocketConns[connIdx].Alias) // rewrite the OriginHost variable if it is empty
chanUUID := fsev.GetUUID()
if missing := fsev.MissingParameter(fsa.timezone); missing != "" {
fsa.disconnectSession(connIdx, chanUUID, "",
utils.NewErrMandatoryIeMissing(missing).Error())
return
for _, val := range []string{utils.AccountField, utils.Subject, utils.Destination, utils.Category, utils.OriginID, utils.Tenant} {
if _, err := cgrEv.FieldAsString(val); err != nil {
utils.Logger.Debug(fmt.Sprintf("error %v", err))
fsa.disconnectSession(connIdx, chanUUID, "",
utils.NewErrMandatoryIeMissing(val).Error())
return
}
}
cgrEv := fsev.AsCGREvent(config.CgrConfig().GeneralCfg().DefaultTimezone)
if cgrEv.APIOpts == nil {
cgrEv.APIOpts = map[string]any{utils.OptsSesInitiate: true}
}
@@ -259,18 +297,27 @@ func (fsa *FSsessions) onChannelAnswer(fsev FSEvent, connIdx int) {
}
func (fsa *FSsessions) onChannelHangupComplete(fsev FSEvent, connIdx int) {
if fsev.GetReqType(utils.MetaDefault) == utils.MetaNone { // Do not process this request
return
}
if connIdx >= len(fsa.conns) { // protection against index out of range panic
err := fmt.Errorf("Index out of range[0,%v): %v ", len(fsa.conns), connIdx)
utils.Logger.Err(fmt.Sprintf("<%s> %s", utils.FreeSWITCHAgent, err.Error()))
return
}
var reply string
fsev[VarCGROriginHost] = utils.FirstNonEmpty(fsev[VarCGROriginHost], fsa.cfg.EventSocketConns[connIdx].Alias) // rewrite the OriginHost variable if it is empty
if fsev[VarAnswerEpoch] != "0" { // call was answered
cgrEv := fsev.AsCGREvent(config.CgrConfig().GeneralCfg().DefaultTimezone)
cgrEv, err := fsa.processRequest(fsev)
if err != nil {
utils.Logger.Err(fmt.Sprintf("<%s> error: %s", utils.FreeSWITCHAgent, err.Error()))
return
}
reqType, err := cgrEv.FieldAsString("RequestType")
if err != nil {
utils.Logger.Err(fmt.Sprintf("<%s> failed to retrieve RequestType: %v", utils.FreeSWITCHAgent, err))
return
}
if reqType == utils.MetaNone {
return // do not process this request
}
if fsev[VarAnswerEpoch] != "0" { // call was answered
if cgrEv.APIOpts == nil {
cgrEv.APIOpts = map[string]any{utils.OptsSesTerminate: true}
}
@@ -281,9 +328,9 @@ func (fsa *FSsessions) onChannelHangupComplete(fsev FSEvent, connIdx int) {
fmt.Sprintf("<%s> Could not terminate session with event %s, error: %s",
utils.FreeSWITCHAgent, fsev.GetUUID(), err.Error()))
}
delete(cgrEv.Event, FsConnID) // Remove the connection ID
}
if fsa.cfg.CreateCDR {
cgrEv := fsev.AsCGREvent(fsa.timezone)
if err := fsa.connMgr.Call(fsa.ctx, fsa.cfg.SessionSConns, utils.SessionSv1ProcessCDR,
cgrEv, &reply); err != nil {
utils.Logger.Err(fmt.Sprintf("<%s> Failed processing CGREvent: %s, error: <%s>",
@@ -493,3 +540,30 @@ func (fsa *FSsessions) V1WarnDisconnect(ctx *context.Context, args map[string]an
*reply = utils.OK
return
}
// processRequest processes the FreeSWITCH event by iterating through request processors,
// applying filters, and setting the required fields.
func (fsa *FSsessions) processRequest(fsev FSEvent) (*utils.CGREvent, error) {
for _, reqProcessor := range fsa.cfg.RequestProcessors {
agReq := NewAgentRequest(
engine.MapStringDP(fsev),
nil, nil, nil, nil,
reqProcessor.Tenant,
fsa.cgrcfg.GeneralCfg().DefaultTenant,
fsa.cgrcfg.GeneralCfg().DefaultTimezone,
fsa.filterS, nil,
)
pass, err := fsa.filterS.Pass(context.TODO(), agReq.Tenant, reqProcessor.Filters, agReq)
if err != nil {
return nil, err
}
if !pass {
continue
}
if err := agReq.SetFields(reqProcessor.RequestFields); err != nil {
return nil, err
}
return utils.NMAsCGREvent(agReq.CGRRequest, agReq.Tenant, utils.NestingSep, agReq.Opts), nil
}
return nil, utils.ErrNotFound
}

View File

@@ -1737,29 +1737,73 @@ const CGRATES_CFG_JSON = `
},
"templates": {
"*fsa": [
{ "tag": "ToR", "path": "*cgreq.ToR","type": "*constant",
"value": "*voice"},
{"tag":"PDD","path":"*cgreq.PDD","type":"*composed",
"value":"~*req.variable_progress_mediamsec;ms"},
{"tag":"ACD","path":"*cgreq.ACD","type":"*composed",
"value":"~*req.variable_cdr_acd;s"},
{"tag": "OriginID","path": "*cgreq.OriginID","type": "*variable",
"value": "~*req.Unique-ID"},
{"tag": "*originID","path": "*opts.*originID","type": "*variable",
"value": "~*req.Unique-ID"},
{"tag": "OriginHost","path": "*cgreq.OriginHost","type": "*variable",
"value": "~*req.variable_cgr_originhost"},
{"tag": "Account","path": "*cgreq.Account","type": "*variable",
"value": "~*req.Caller-Username"},
{"tag":"Source","path":"*cgreq.Source", "type":"*composed",
"value":"FS_;~*req.Event-Name"},
{"tag":"RequestType","path":"*cgreq.RequestType","type":"*constant",
"value":"*none","filters":["*string:*req.variable_process_cdr:false"]},
{"tag":"RequestType","path":"*cgreq.RequestType","type":"*constant",
"value":"*none","filters":["*string:*req.Caller-Dialplan:inline"]},
{"tag":"RequestType","path": "*cgreq.RequestType","type": "*constant",
"filters":["*exists:*cgreq.RequestType:"],"value": "*prepaid"},
{"tag":"Tenant","path":"*cgreq.Tenant","type":"*constant",
"value":"cgrates.org"},
{"tag":"Category","path":"*cgreq.Category","type":"*constant",
"value":"call"},
{"tag":"Subject","path":"*cgreq.Subject","type":"*variable",
"value":"~*req.Caller-Username"},
{"tag":"Destination","path":"*cgreq.Destination","type":"*variable",
"value":"~*req.Caller-Destination-Number"},
{"tag":"SetupTime","path":"*cgreq.SetupTime","type":"*variable",
"value":"~*req.Caller-Channel-Created-Time"},
{"tag":"AnswerTime","path":"*cgreq.AnswerTime","type":"*variable",
"value":"~*req.Caller-Channel-Answered-Time"},
{"tag":"Usage","path":"*cgreq.Usage","type":"*composed",
"value":"~*req.variable_billsec;s"},
{"tag":"Route","path":"*cgreq.Route","type":"*variable",
"value":"~*req.variable_cgr_route"},
{"tag":"Cost","path":"*cgreq.Cost","type":"*constant",
"value":"-1.0"},
{"tag":"DisconnectCause","path":"*cgreq.DisconnectCause",
"filters":["*notempty:*req.Hangup-Cause:"],"type":"*variable","value":"~*req.Hangup-Cause"},
],
"*err": [
{"tag": "SessionId", "path": "*rep.Session-Id", "type": "*variable",
"value": "~*req.Session-Id", "mandatory": true},
{"tag": "OriginHost", "path": "*rep.Origin-Host", "type": "*variable",
"value": "~*vars.OriginHost", "mandatory": true},
{"tag": "OriginRealm", "path": "*rep.Origin-Realm", "type": "*variable",
"value": "~*vars.OriginRealm", "mandatory": true}
{"tag": "SessionId", "path": "*rep.Session-Id", "type": "*variable",
"value": "~*req.Session-Id", "mandatory": true},
{"tag": "OriginHost", "path": "*rep.Origin-Host", "type": "*variable",
"value": "~*vars.OriginHost", "mandatory": true},
{"tag": "OriginRealm", "path": "*rep.Origin-Realm", "type": "*variable",
"value": "~*vars.OriginRealm", "mandatory": true}
],
"*cca": [
{"tag": "SessionId", "path": "*rep.Session-Id", "type": "*variable",
"value": "~*req.Session-Id", "mandatory": true},
{"tag": "ResultCode", "path": "*rep.Result-Code", "type": "*constant",
"value": "2001"},
{"tag": "OriginHost", "path": "*rep.Origin-Host", "type": "*variable",
"value": "~*vars.OriginHost", "mandatory": true},
{"tag": "OriginRealm", "path": "*rep.Origin-Realm", "type": "*variable",
"value": "~*vars.OriginRealm", "mandatory": true},
{"tag": "AuthApplicationId", "path": "*rep.Auth-Application-Id", "type": "*variable",
"value": "~*vars.*appid", "mandatory": true},
{"tag": "CCRequestType", "path": "*rep.CC-Request-Type", "type": "*variable",
"value": "~*req.CC-Request-Type", "mandatory": true},
{"tag": "CCRequestNumber", "path": "*rep.CC-Request-Number", "type": "*variable",
"value": "~*req.CC-Request-Number", "mandatory": true}
{"tag": "SessionId", "path": "*rep.Session-Id", "type": "*variable",
"value": "~*req.Session-Id", "mandatory": true},
{"tag": "ResultCode", "path": "*rep.Result-Code", "type": "*constant",
"value": "2001"},
{"tag": "OriginHost", "path": "*rep.Origin-Host", "type": "*variable",
"value": "~*vars.OriginHost", "mandatory": true},
{"tag": "OriginRealm", "path": "*rep.Origin-Realm", "type": "*variable",
"value": "~*vars.OriginRealm", "mandatory": true},
{"tag": "AuthApplicationId", "path": "*rep.Auth-Application-Id", "type": "*variable",
"value": "~*vars.*appid", "mandatory": true},
{"tag": "CCRequestType", "path": "*rep.CC-Request-Type", "type": "*variable",
"value": "~*req.CC-Request-Type", "mandatory": true},
{"tag": "CCRequestNumber", "path": "*rep.CC-Request-Number", "type": "*variable",
"value": "~*req.CC-Request-Number", "mandatory": true}
],
"*asr": [
{"tag": "SessionId", "path": "*diamreq.Session-Id", "type": "*variable",

View File

@@ -2071,6 +2071,117 @@ func TestDfRateSJsonCfg(t *testing.T) {
func TestDfTemplateSJsonCfg(t *testing.T) {
eCfg := FcTemplatesJsonCfg{
"*fsa": {
{
Tag: utils.StringPointer("ToR"),
Path: utils.StringPointer("*cgreq.ToR"),
Type: utils.StringPointer(utils.MetaConstant),
Value: utils.StringPointer(utils.MetaVoice)},
{
Tag: utils.StringPointer("PDD"),
Path: utils.StringPointer("*cgreq.PDD"),
Type: utils.StringPointer(utils.MetaComposed),
Value: utils.StringPointer("~*req.variable_progress_mediamsec;ms")},
{
Tag: utils.StringPointer("ACD"),
Path: utils.StringPointer("*cgreq.ACD"),
Type: utils.StringPointer(utils.MetaComposed),
Value: utils.StringPointer("~*req.variable_cdr_acd;s")},
{
Tag: utils.StringPointer("OriginID"),
Path: utils.StringPointer("*cgreq.OriginID"),
Type: utils.StringPointer(utils.MetaVariable),
Value: utils.StringPointer("~*req.Unique-ID")},
{
Tag: utils.StringPointer("*originID"),
Path: utils.StringPointer("*opts.*originID"),
Type: utils.StringPointer(utils.MetaVariable),
Value: utils.StringPointer("~*req.Unique-ID")},
{
Tag: utils.StringPointer("OriginHost"),
Path: utils.StringPointer("*cgreq.OriginHost"),
Type: utils.StringPointer(utils.MetaVariable),
Value: utils.StringPointer("~*req.variable_cgr_originhost")},
{
Tag: utils.StringPointer("Account"),
Path: utils.StringPointer("*cgreq.Account"),
Type: utils.StringPointer(utils.MetaVariable),
Value: utils.StringPointer("~*req.Caller-Username")},
{
Tag: utils.StringPointer("Source"),
Path: utils.StringPointer("*cgreq.Source"),
Type: utils.StringPointer(utils.MetaComposed),
Value: utils.StringPointer("FS_;~*req.Event-Name")},
{
Tag: utils.StringPointer("RequestType"),
Path: utils.StringPointer("*cgreq.RequestType"),
Type: utils.StringPointer(utils.MetaConstant),
Filters: &[]string{"*string:*req.variable_process_cdr:false"},
Value: utils.StringPointer("*none")},
{
Tag: utils.StringPointer("RequestType"),
Path: utils.StringPointer("*cgreq.RequestType"),
Type: utils.StringPointer(utils.MetaConstant),
Filters: &[]string{"*string:*req.Caller-Dialplan:inline"},
Value: utils.StringPointer("*none")},
{
Tag: utils.StringPointer("RequestType"),
Path: utils.StringPointer("*cgreq.RequestType"),
Type: utils.StringPointer(utils.MetaConstant),
Filters: &[]string{"*exists:*cgreq.RequestType:"},
Value: utils.StringPointer("*prepaid")},
{
Tag: utils.StringPointer("Tenant"),
Path: utils.StringPointer("*cgreq.Tenant"),
Type: utils.StringPointer(utils.MetaConstant),
Value: utils.StringPointer("cgrates.org")},
{
Tag: utils.StringPointer("Category"),
Path: utils.StringPointer("*cgreq.Category"),
Type: utils.StringPointer(utils.MetaConstant),
Value: utils.StringPointer("call")},
{
Tag: utils.StringPointer("Subject"),
Path: utils.StringPointer("*cgreq.Subject"),
Type: utils.StringPointer(utils.MetaVariable),
Value: utils.StringPointer("~*req.Caller-Username")},
{
Tag: utils.StringPointer("Destination"),
Path: utils.StringPointer("*cgreq.Destination"),
Type: utils.StringPointer(utils.MetaVariable),
Value: utils.StringPointer("~*req.Caller-Destination-Number")},
{
Tag: utils.StringPointer("SetupTime"),
Path: utils.StringPointer("*cgreq.SetupTime"),
Type: utils.StringPointer(utils.MetaVariable),
Value: utils.StringPointer("~*req.Caller-Channel-Created-Time")},
{
Tag: utils.StringPointer("AnswerTime"),
Path: utils.StringPointer("*cgreq.AnswerTime"),
Type: utils.StringPointer(utils.MetaVariable),
Value: utils.StringPointer("~*req.Caller-Channel-Answered-Time")},
{
Tag: utils.StringPointer("Usage"),
Path: utils.StringPointer("*cgreq.Usage"),
Type: utils.StringPointer(utils.MetaComposed),
Value: utils.StringPointer("~*req.variable_billsec;s")},
{
Tag: utils.StringPointer("Route"),
Path: utils.StringPointer("*cgreq.Route"),
Type: utils.StringPointer(utils.MetaVariable),
Value: utils.StringPointer("~*req.variable_cgr_route")},
{
Tag: utils.StringPointer("Cost"),
Path: utils.StringPointer("*cgreq.Cost"),
Type: utils.StringPointer(utils.MetaConstant),
Value: utils.StringPointer("-1.0")},
{
Tag: utils.StringPointer("DisconnectCause"),
Path: utils.StringPointer("*cgreq.DisconnectCause"),
Type: utils.StringPointer(utils.MetaVariable),
Filters: &[]string{"*notempty:*req.Hangup-Cause:"},
Value: utils.StringPointer("~*req.Hangup-Cause")},
},
"*errSip": {
{
Tag: utils.StringPointer("Request"),

File diff suppressed because one or more lines are too long

View File

@@ -104,6 +104,7 @@ type FsAgentCfg struct {
MaxWaitConnection time.Duration
ActiveSessionDelimiter string
EventSocketConns []*FsConnCfg
RequestProcessors []*RequestProcessor
}
// loadFreeswitchAgentCfg loads the FreeswitchAgent section of the configuration
@@ -162,6 +163,7 @@ func (fscfg *FsAgentCfg) loadFromJSONCfg(jsnCfg *FreeswitchAgentJsonCfg) error {
fscfg.EventSocketConns[idx].loadFromJSONCfg(jsnConnCfg)
}
}
fscfg.RequestProcessors, err = appendRequestProcessors(fscfg.RequestProcessors, jsnCfg.Request_processors)
return nil
}
@@ -180,6 +182,11 @@ func (fscfg FsAgentCfg) AsMapInterface() any {
if fscfg.SessionSConns != nil {
mp[utils.SessionSConnsCfg] = getBiRPCInternalJSONConns(fscfg.SessionSConns)
}
requestProcessors := make([]map[string]any, len(fscfg.RequestProcessors))
for i, item := range fscfg.RequestProcessors {
requestProcessors[i] = item.AsMapInterface()
}
mp[utils.RequestProcessorsCfg] = requestProcessors
if fscfg.ExtraFields != nil {
mp[utils.ExtraFieldsCfg] = fscfg.ExtraFields.AsStringSlice()
}
@@ -220,6 +227,12 @@ func (fscfg FsAgentCfg) Clone() (cln *FsAgentCfg) {
cln.EventSocketConns[i] = req.Clone()
}
}
if fscfg.RequestProcessors != nil {
cln.RequestProcessors = make([]*RequestProcessor, len(fscfg.RequestProcessors))
for i, req := range fscfg.RequestProcessors {
cln.RequestProcessors[i] = req.Clone()
}
}
return
}
@@ -236,6 +249,7 @@ type FreeswitchAgentJsonCfg struct {
MaxWaitConnection *string `json:"max_wait_connection"`
ActiveSessionDelimiter *string `json:"active_session_delimiter"`
EventSocketConns *[]*FsConnJsonCfg `json:"event_socket_conns"`
Request_processors *[]*ReqProcessorJsnCfg
}
// Represents one connection instance towards FreeSWITCH

View File

@@ -829,6 +829,7 @@ func TestFsAgentCfgAsMapInterfaceCase1(t *testing.T) {
utils.AliasCfg: "127.0.0.1:8021",
},
},
utils.RequestProcessorsCfg: []map[string]any{},
}
if cgrCfg, err := NewCGRConfigFromJSONStringWithDefaults(cfgJSONStr); err != nil {
t.Error(err)
@@ -871,6 +872,7 @@ func TestFsAgentCfgAsMapInterfaceCase2(t *testing.T) {
utils.AliasCfg: "127.0.0.1:8000",
},
},
utils.RequestProcessorsCfg: []map[string]any{},
}
if cgrCfg, err := NewCGRConfigFromJSONStringWithDefaults(cfgJSONStr); err != nil {
t.Error(err)
@@ -908,6 +910,7 @@ func TestFsAgentCfgAsMapInterfaceCase3(t *testing.T) {
utils.ReplyTimeoutCfg: "1m0s",
},
},
utils.RequestProcessorsCfg: []map[string]any{},
}
if cgrCfg, err := NewCGRConfigFromJSONStringWithDefaults(cfgJSONStr); err != nil {
t.Error(err)

View File

@@ -67,7 +67,20 @@
{"address": "127.0.0.1:8021", "password": "ClueCon", "reconnects": -1,"alias":""}
],
"sessions_conns": ["*birpc_internal"],
"create_cdr": true
"create_cdr": true,
"request_processors": [
{
"id": "FSEvent",
"request_fields":[
{
"tag": "BaseTmpl",
"type": "*template",
"value": "*fsa"
}
]
}
]
},

View File

@@ -66,7 +66,20 @@
{"address": "127.0.0.1:8021", "password": "ClueCon", "reconnects": -1,"alias":""}
],
"sessions_conns": ["*birpc_internal"],
"create_cdr": true
"create_cdr": true,
"request_processors": [
{
"id": "FSEvent",
"request_fields":[
{
"tag": "BaseTmpl",
"type": "*template",
"value": "*fsa"
}
]
}
]
},

View File

@@ -210,3 +210,26 @@ func (me MapEvent) GetBoolOrDefault(fldName string, dflt bool) (out bool) {
}
return out
}
type MapStringDP map[string]string // map storage for string values
func (me MapStringDP) String() string {
return utils.ToJSON(me)
}
func (me MapStringDP) FieldAsInterface(fldPath []string) (any, error) {
if len(fldPath) != 1 {
return nil, utils.ErrNotFound
}
fldIface, has := me[fldPath[0]]
if !has {
return nil, utils.ErrNotFound
}
return fldIface, nil
}
func (me MapStringDP) FieldAsString(fldPath []string) (string, error) {
if len(fldPath) != 1 {
return "", utils.ErrNotFound
}
return fldPath[0], nil
}

View File

@@ -46,16 +46,25 @@ type FreeswitchAgent struct {
// Start should handle the sercive start
func (fS *FreeswitchAgent) Start(shutdown *utils.SyncedChan, registry *servmanager.ServiceRegistry) (err error) {
cms, err := WaitForServiceState(utils.StateServiceUP, utils.ConnManager, registry, fS.cfg.GeneralCfg().ConnectTimeout)
srvDeps, err := WaitForServicesToReachState(utils.StateServiceUP,
[]string{
utils.ConnManager,
utils.FilterS,
},
registry, fS.cfg.GeneralCfg().ConnectTimeout)
if err != nil {
return
}
cms := srvDeps[utils.ConnManager].(*ConnManagerService)
fs := srvDeps[utils.FilterS].(*FilterService)
fS.Lock()
defer fS.Unlock()
fS.fS = agents.NewFSsessions(fS.cfg.FsAgentCfg(), fS.cfg.GeneralCfg().DefaultTimezone, cms.(*ConnManagerService).ConnManager())
fS.fS, err = agents.NewFSsessions(fs.cfg, fs.FilterS(), fS.cfg.GeneralCfg().DefaultTimezone, cms.ConnManager())
if err != nil {
return
}
go fS.connect(shutdown)
return
}