diff --git a/agents/astagent.go b/agents/astagent.go
index e8630ed4d..4a7762e32 100644
--- a/agents/astagent.go
+++ b/agents/astagent.go
@@ -249,8 +249,10 @@ func (sma *AsteriskAgent) handleChannelStateChange(ev *SMAsteriskEvent) {
if !hasIt { // Not handled by us
return
}
+
+ // Update the cached event with new channel state.
sma.evCacheMux.Lock()
- err := ev.UpdateCGREvent(cgrEvDisp.CGREvent) // Updates the event directly in the cache
+ err := ev.UpdateCGREvent(cgrEvDisp.CGREvent, sma.cgrCfg.AsteriskAgentCfg().AlterableFields)
sma.evCacheMux.Unlock()
if err != nil {
sma.hangupChannel(ev.ChannelID(),
@@ -258,6 +260,7 @@ func (sma *AsteriskAgent) handleChannelStateChange(ev *SMAsteriskEvent) {
utils.AsteriskAgent, err.Error(), ev.ChannelID()))
return
}
+
// populate init session args
initSessionArgs := ev.V1InitSessionArgs(*cgrEvDisp)
if initSessionArgs == nil {
@@ -289,8 +292,10 @@ func (sma *AsteriskAgent) handleChannelDestroyed(ev *SMAsteriskEvent) {
if !hasIt { // Not handled by us
return
}
+
+ // Update the cached event with new channel state.
sma.evCacheMux.Lock()
- err := ev.UpdateCGREvent(cgrEvDisp.CGREvent) // Updates the event directly in the cache
+ err := ev.UpdateCGREvent(cgrEvDisp.CGREvent, sma.cgrCfg.AsteriskAgentCfg().AlterableFields)
sma.evCacheMux.Unlock()
if err != nil {
utils.Logger.Warning(
@@ -298,6 +303,7 @@ func (sma *AsteriskAgent) handleChannelDestroyed(ev *SMAsteriskEvent) {
utils.AsteriskAgent, err.Error(), ev.ChannelID()))
return
}
+
// populate terminate session args
tsArgs := ev.V1TerminateSessionArgs(*cgrEvDisp)
if tsArgs == nil {
diff --git a/agents/asterisk_event.go b/agents/asterisk_event.go
index 8c7d3d697..cf3336e0d 100644
--- a/agents/asterisk_event.go
+++ b/agents/asterisk_event.go
@@ -19,6 +19,7 @@ along with this program. If not, see
package agents
import (
+ "errors"
"fmt"
"strings"
@@ -186,7 +187,8 @@ func (smaEv *SMAsteriskEvent) ExtraParameters() (extraParams map[string]string)
return
}
-func (smaEv *SMAsteriskEvent) UpdateCGREvent(cgrEv *utils.CGREvent) error {
+// UpdateCGREvent updates a previously cached CGREvent (from StasisStart) with data from a new ARI event.
+func (smaEv *SMAsteriskEvent) UpdateCGREvent(cgrEv *utils.CGREvent, alterableFields []string) error {
resCGREv := *cgrEv
switch smaEv.EventType() {
case ARIChannelStateChange:
@@ -212,6 +214,30 @@ func (smaEv *SMAsteriskEvent) UpdateCGREvent(cgrEv *utils.CGREvent) error {
}
}
}
+
+ // Update fields specified in alterableFields. Each field can be in the format:
+ // - "path.to.field": copies the field value using the last path element as key
+ // - "path.to.field:alias": copies the field value using the alias as key
+ if len(alterableFields) != 0 {
+ ms := utils.MapStorage(smaEv.ariEv)
+ for _, field := range alterableFields {
+ path, alias, hasAlias := strings.Cut(field, utils.InInFieldSep)
+ pathElems := strings.Split(path, utils.NestingSep)
+ fieldVal, err := ms.FieldAsString(pathElems)
+ if errors.Is(err, utils.ErrNotFound) {
+ continue
+ }
+ if err != nil {
+ return err
+ }
+ fieldKey := pathElems[len(pathElems)-1]
+ if hasAlias {
+ fieldKey = alias
+ }
+ resCGREv.Event[fieldKey] = fieldVal
+ }
+ }
+
*cgrEv = resCGREv
return nil
}
diff --git a/config/config_defaults.go b/config/config_defaults.go
index 8aec081fb..be92860bb 100644
--- a/config/config_defaults.go
+++ b/config/config_defaults.go
@@ -369,15 +369,19 @@ const CGRATES_CFG_JSON = `
"asterisk_agent": {
- "enabled": false, // starts the Asterisk agent:
+ "enabled": false, // starts the Asterisk agent:
"sessions_conns": ["*internal"],
- "create_cdr": false, // create CDR out of events and sends it to CDRS component
- "asterisk_conns":[ // instantiate connections to multiple Asterisk servers
- {"address": "127.0.0.1:8088", "user": "cgrates", "password": "CGRateS.org", "connect_attempts": 3,"reconnects": 5}
- ],
+ "create_cdr": false, // create CDR out of events and sends it to CDRS component
+ //"alterable_fields": [], // additional fields to update from ARI events, optionally with :alias
+ "asterisk_conns": [{ // instantiate connections to multiple Asterisk servers
+ "address": "127.0.0.1:8088",
+ "user": "cgrates",
+ "password": "CGRateS.org",
+ "connect_attempts": 3,
+ "reconnects": 5
+ }]
},
-
"freeswitch_agent": {
"enabled": false, // starts the FreeSWITCH agent:
"sessions_conns": ["*internal"],
diff --git a/config/libconfig_json.go b/config/libconfig_json.go
index 1c0ec0ed6..1b8e1dedf 100644
--- a/config/libconfig_json.go
+++ b/config/libconfig_json.go
@@ -268,10 +268,11 @@ type AstConnJsonCfg struct {
}
type AsteriskAgentJsonCfg struct {
- Enabled *bool
- Sessions_conns *[]string
- Create_cdr *bool
- Asterisk_conns *[]*AstConnJsonCfg
+ Enabled *bool
+ Sessions_conns *[]string
+ Create_cdr *bool
+ Alterable_fields *[]string
+ Asterisk_conns *[]*AstConnJsonCfg
}
type CacheParamJsonCfg struct {
diff --git a/config/smconfig.go b/config/smconfig.go
index b07295b80..5de32c7b5 100644
--- a/config/smconfig.go
+++ b/config/smconfig.go
@@ -20,6 +20,7 @@ package config
import (
"fmt"
+ "slices"
"strconv"
"strings"
"time"
@@ -598,10 +599,11 @@ func (aConnCfg *AsteriskConnCfg) AsMapInterface() map[string]any {
}
type AsteriskAgentCfg struct {
- Enabled bool
- SessionSConns []string
- CreateCDR bool
- AsteriskConns []*AsteriskConnCfg
+ Enabled bool
+ SessionSConns []string
+ CreateCDR bool
+ AlterableFields []string
+ AsteriskConns []*AsteriskConnCfg
}
func (aCfg *AsteriskAgentCfg) loadFromJsonCfg(jsnCfg *AsteriskAgentJsonCfg) (err error) {
@@ -625,6 +627,9 @@ func (aCfg *AsteriskAgentCfg) loadFromJsonCfg(jsnCfg *AsteriskAgentJsonCfg) (err
if jsnCfg.Create_cdr != nil {
aCfg.CreateCDR = *jsnCfg.Create_cdr
}
+ if jsnCfg.Alterable_fields != nil {
+ aCfg.AlterableFields = *jsnCfg.Alterable_fields
+ }
if jsnCfg.Asterisk_conns != nil {
aCfg.AsteriskConns = make([]*AsteriskConnCfg, len(*jsnCfg.Asterisk_conns))
for i, jsnAConn := range *jsnCfg.Asterisk_conns {
@@ -652,9 +657,10 @@ func (aCfg *AsteriskAgentCfg) AsMapInterface() map[string]any {
}
return map[string]any{
- utils.EnabledCfg: aCfg.Enabled,
- utils.SessionSConnsCfg: sessionSConns,
- utils.CreateCDRCfg: aCfg.CreateCDR,
- utils.AsteriskConnsCfg: conns,
+ utils.EnabledCfg: aCfg.Enabled,
+ utils.SessionSConnsCfg: sessionSConns,
+ utils.CreateCDRCfg: aCfg.CreateCDR,
+ utils.AlterableFieldsCfg: slices.Clone(aCfg.AlterableFields),
+ utils.AsteriskConnsCfg: conns,
}
}
diff --git a/config/smconfig_test.go b/config/smconfig_test.go
index 844e0f0ae..1b635a176 100644
--- a/config/smconfig_test.go
+++ b/config/smconfig_test.go
@@ -497,15 +497,17 @@ func TestAsteriskAgentCfgAsMapInterface(t *testing.T) {
"enabled": true,
"sessions_conns": ["*internal"],
"create_cdr": false,
+ "alterable_fields": ["field1", "field2"],
"asterisk_conns":[
{"address": "127.0.0.1:8088", "user": "cgrates", "password": "CGRateS.org", "connect_attempts": 3,"reconnects": 5}
],
},
}`
eMap := map[string]any{
- "enabled": true,
- "sessions_conns": []string{"*internal"},
- "create_cdr": false,
+ "enabled": true,
+ "sessions_conns": []string{"*internal"},
+ "create_cdr": false,
+ utils.AlterableFieldsCfg: []string{"field1", "field2"},
"asterisk_conns": []map[string]any{
{"alias": "", "address": "127.0.0.1:8088", "user": "cgrates", "password": "CGRateS.org", "connect_attempts": 3, "reconnects": 5},
},
@@ -517,7 +519,7 @@ func TestAsteriskAgentCfgAsMapInterface(t *testing.T) {
} else if err = asagcfg.loadFromJsonCfg(jsnAsAgCfg); err != nil {
t.Error(err)
} else if rcv := asagcfg.AsMapInterface(); !reflect.DeepEqual(eMap, rcv) {
- t.Errorf("\nExpected: %+v\nReceived: %+v", utils.ToJSON(eMap), utils.ToJSON(rcv))
+ t.Errorf("expected: %s, received: %s", utils.ToJSON(eMap), utils.ToJSON(rcv))
}
}
@@ -912,9 +914,10 @@ func TestSMConfigAsteriskAgentCfgloadFromJsonCfg(t *testing.T) {
func TestSMConfigAsteriskAgentCfgAsMapInterface(t *testing.T) {
str := "test"
aCfg := &AsteriskAgentCfg{
- Enabled: false,
- SessionSConns: []string{str},
- CreateCDR: false,
+ Enabled: false,
+ SessionSConns: []string{str},
+ CreateCDR: false,
+ AlterableFields: []string{"field1", "field2"},
AsteriskConns: []*AsteriskConnCfg{
{
Alias: str,
@@ -927,15 +930,14 @@ func TestSMConfigAsteriskAgentCfgAsMapInterface(t *testing.T) {
},
}
exp := map[string]any{
- utils.EnabledCfg: aCfg.Enabled,
- utils.SessionSConnsCfg: []string{str},
- utils.CreateCDRCfg: aCfg.CreateCDR,
- utils.AsteriskConnsCfg: []map[string]any{aCfg.AsteriskConns[0].AsMapInterface()},
+ utils.EnabledCfg: aCfg.Enabled,
+ utils.SessionSConnsCfg: []string{str},
+ utils.CreateCDRCfg: aCfg.CreateCDR,
+ utils.AlterableFieldsCfg: []string{"field1", "field2"},
+ utils.AsteriskConnsCfg: []map[string]any{aCfg.AsteriskConns[0].AsMapInterface()},
}
- rcv := aCfg.AsMapInterface()
-
- if !reflect.DeepEqual(exp, rcv) {
+ if rcv := aCfg.AsMapInterface(); !reflect.DeepEqual(exp, rcv) {
t.Errorf("expected %s, received %s", utils.ToJSON(exp), utils.ToJSON(rcv))
}
}