cfg: add stats/thresholds_conns to http_agent

This commit is contained in:
ionutboangiu
2025-06-06 17:35:02 +03:00
committed by Dan Christian Bogos
parent 89f7e51199
commit fc905f0e9c
5 changed files with 120 additions and 86 deletions

View File

@@ -1343,7 +1343,7 @@ func TestLoadHttpAgentCfgError(t *testing.T) {
},
],
}`
expected := "json: cannot unmarshal array into Go struct field HttpAgentJsonCfg.Id of type string"
expected := "json: cannot unmarshal array into Go struct field HttpAgentJsonCfg.id of type string"
cgrConfig := NewDefaultCGRConfig()
if cgrCfgJSON, err := NewCgrJsonCfgFromBytes([]byte(cfgJSONStr)); err != nil {
t.Error(err)

View File

@@ -528,6 +528,24 @@ func (cfg *CGRConfig) checkConfigSanity() error {
return fmt.Errorf("<%s> template with ID <%s> has connection with id: <%s> not defined", utils.HTTPAgent, httpAgentCfg.ID, connID)
}
}
for _, connID := range httpAgentCfg.StatSConns {
isInternal := strings.HasPrefix(connID, utils.MetaInternal) || strings.HasPrefix(connID, rpcclient.BiRPCInternal)
if isInternal && !cfg.sessionSCfg.Enabled {
return fmt.Errorf("<%s> not enabled but requested by <%s> HTTPAgent Template", utils.StatS, httpAgentCfg.ID)
}
if _, has := cfg.rpcConns[connID]; !has && !isInternal {
return fmt.Errorf("<%s> template with ID <%s> has connection with id: <%s> not defined", utils.HTTPAgent, httpAgentCfg.ID, connID)
}
}
for _, connID := range httpAgentCfg.ThresholdSConns {
isInternal := strings.HasPrefix(connID, utils.MetaInternal) || strings.HasPrefix(connID, rpcclient.BiRPCInternal)
if isInternal && !cfg.sessionSCfg.Enabled {
return fmt.Errorf("<%s> not enabled but requested by <%s> HTTPAgent Template", utils.ThresholdS, httpAgentCfg.ID)
}
if _, has := cfg.rpcConns[connID]; !has && !isInternal {
return fmt.Errorf("<%s> template with ID <%s> has connection with id: <%s> not defined", utils.HTTPAgent, httpAgentCfg.ID, connID)
}
}
if !slices.Contains([]string{utils.MetaUrl, utils.MetaXml}, httpAgentCfg.RequestPayload) {
return fmt.Errorf("<%s> unsupported request payload %s", utils.HTTPAgent, httpAgentCfg.RequestPayload)
}

View File

@@ -19,6 +19,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package config
import (
"slices"
"github.com/cgrates/cgrates/utils"
"github.com/cgrates/rpcclient"
)
@@ -33,9 +35,9 @@ func (hcfgs *HTTPAgentCfgs) loadFromJSONCfg(jsnHTTPAgntCfg *[]*HttpAgentJsonCfg,
for _, jsnCfg := range *jsnHTTPAgntCfg {
hac := new(HTTPAgentCfg)
var haveID bool
if jsnCfg.Id != nil {
if jsnCfg.ID != nil {
for _, val := range *hcfgs {
if val.ID == *jsnCfg.Id {
if val.ID == *jsnCfg.ID {
hac = val
haveID = true
break
@@ -79,6 +81,8 @@ type HTTPAgentCfg struct {
ID string // identifier for the agent, so we can update it's processors
URL string
SessionSConns []string
StatSConns []string
ThresholdSConns []string
RequestPayload string
ReplyPayload string
RequestProcessors []*RequestProcessor
@@ -114,15 +118,15 @@ func (ha *HTTPAgentCfg) loadFromJSONCfg(jsnCfg *HttpAgentJsonCfg, separator stri
if jsnCfg == nil {
return nil
}
if jsnCfg.Id != nil {
ha.ID = *jsnCfg.Id
if jsnCfg.ID != nil {
ha.ID = *jsnCfg.ID
}
if jsnCfg.Url != nil {
ha.URL = *jsnCfg.Url
if jsnCfg.URL != nil {
ha.URL = *jsnCfg.URL
}
if jsnCfg.Sessions_conns != nil {
ha.SessionSConns = make([]string, len(*jsnCfg.Sessions_conns))
for idx, connID := range *jsnCfg.Sessions_conns {
if jsnCfg.SessionSConns != nil {
ha.SessionSConns = make([]string, len(*jsnCfg.SessionSConns))
for idx, connID := range *jsnCfg.SessionSConns {
// if we have the connection internal we change the name so we can have internal rpc for each subsystem
ha.SessionSConns[idx] = connID
if connID == utils.MetaInternal ||
@@ -131,27 +135,39 @@ func (ha *HTTPAgentCfg) loadFromJSONCfg(jsnCfg *HttpAgentJsonCfg, separator stri
}
}
}
if jsnCfg.Request_payload != nil {
ha.RequestPayload = *jsnCfg.Request_payload
if jsnCfg.StatSConns != nil {
ha.StatSConns = tagInternalConns(*jsnCfg.StatSConns, utils.MetaStats)
}
if jsnCfg.Reply_payload != nil {
ha.ReplyPayload = *jsnCfg.Reply_payload
if jsnCfg.ThresholdSConns != nil {
ha.ThresholdSConns = tagInternalConns(*jsnCfg.ThresholdSConns, utils.MetaThresholds)
}
if err = ha.appendHTTPAgntProcCfgs(jsnCfg.Request_processors, separator); err != nil {
if jsnCfg.RequestPayload != nil {
ha.RequestPayload = *jsnCfg.RequestPayload
}
if jsnCfg.ReplyPayload != nil {
ha.ReplyPayload = *jsnCfg.ReplyPayload
}
if err = ha.appendHTTPAgntProcCfgs(jsnCfg.RequestProcessors, separator); err != nil {
return err
}
return nil
}
// AsMapInterface returns the config as a map[string]any
func (ha *HTTPAgentCfg) AsMapInterface(separator string) (initialMP map[string]any) {
initialMP = map[string]any{
utils.IDCfg: ha.ID,
utils.URLCfg: ha.URL,
utils.RequestPayloadCfg: ha.RequestPayload,
utils.ReplyPayloadCfg: ha.ReplyPayload,
func (ha *HTTPAgentCfg) AsMapInterface(separator string) map[string]any {
requestProcessors := make([]map[string]any, len(ha.RequestProcessors))
for i, item := range ha.RequestProcessors {
requestProcessors[i] = item.AsMapInterface(separator)
}
m := map[string]any{
utils.IDCfg: ha.ID,
utils.URLCfg: ha.URL,
utils.RequestPayloadCfg: ha.RequestPayload,
utils.ReplyPayloadCfg: ha.ReplyPayload,
utils.StatSConnsCfg: stripInternalConns(ha.StatSConns),
utils.ThresholdSConnsCfg: stripInternalConns(ha.ThresholdSConns),
utils.RequestProcessorsCfg: requestProcessors,
}
if ha.SessionSConns != nil {
sessionSConns := make([]string, len(ha.SessionSConns))
for i, item := range ha.SessionSConns {
@@ -162,31 +178,25 @@ func (ha *HTTPAgentCfg) AsMapInterface(separator string) (initialMP map[string]a
sessionSConns[i] = rpcclient.BiRPCInternal
}
}
initialMP[utils.SessionSConnsCfg] = sessionSConns
m[utils.SessionSConnsCfg] = sessionSConns
}
requestProcessors := make([]map[string]any, len(ha.RequestProcessors))
for i, item := range ha.RequestProcessors {
requestProcessors[i] = item.AsMapInterface(separator)
}
initialMP[utils.RequestProcessorsCfg] = requestProcessors
return
return m
}
// Clone returns a deep copy of HTTPAgentCfg
func (ha HTTPAgentCfg) Clone() (cln *HTTPAgentCfg) {
cln = &HTTPAgentCfg{
func (ha HTTPAgentCfg) Clone() *HTTPAgentCfg {
clone := &HTTPAgentCfg{
ID: ha.ID,
URL: ha.URL,
SessionSConns: slices.Clone(ha.SessionSConns),
StatSConns: slices.Clone(ha.StatSConns),
ThresholdSConns: slices.Clone(ha.ThresholdSConns),
RequestPayload: ha.RequestPayload,
ReplyPayload: ha.ReplyPayload,
RequestProcessors: make([]*RequestProcessor, len(ha.RequestProcessors)),
}
if ha.SessionSConns != nil {
cln.SessionSConns = make([]string, len(ha.SessionSConns))
copy(cln.SessionSConns, ha.SessionSConns)
}
for i, req := range ha.RequestProcessors {
cln.RequestProcessors[i] = req.Clone()
clone.RequestProcessors[i] = req.Clone()
}
return
return clone
}

View File

@@ -29,12 +29,12 @@ import (
func TestHttpAgentCfgsloadFromJsonCfgCase1(t *testing.T) {
cfgJSON := &[]*HttpAgentJsonCfg{
{
Id: utils.StringPointer("RandomID"),
Url: utils.StringPointer("/randomURL"),
Sessions_conns: &[]string{"*internal"},
Reply_payload: utils.StringPointer(utils.MetaXml),
Request_payload: utils.StringPointer(utils.MetaUrl),
Request_processors: &[]*ReqProcessorJsnCfg{
ID: utils.StringPointer("RandomID"),
URL: utils.StringPointer("/randomURL"),
SessionSConns: &[]string{"*internal"},
ReplyPayload: utils.StringPointer(utils.MetaXml),
RequestPayload: utils.StringPointer(utils.MetaUrl),
RequestProcessors: &[]*ReqProcessorJsnCfg{
{
ID: utils.StringPointer("OutboundAUTHDryRun"),
Filters: &[]string{"*string:*req.request_type:OutboundAUTH", "*string:*req.Msisdn:497700056231"},
@@ -91,12 +91,12 @@ func TestHttpAgentCfgsloadFromJsonCfgCase1(t *testing.T) {
func TestHttpAgentCfgsloadFromJsonCfgCase2(t *testing.T) {
cfgJSON := &[]*HttpAgentJsonCfg{
{
Id: utils.StringPointer("conecto1"),
Url: utils.StringPointer("/conecto"),
Sessions_conns: &[]string{utils.MetaLocalHost},
Request_payload: utils.StringPointer(utils.MetaUrl),
Reply_payload: utils.StringPointer(utils.MetaXml),
Request_processors: &[]*ReqProcessorJsnCfg{
ID: utils.StringPointer("conecto1"),
URL: utils.StringPointer("/conecto"),
SessionSConns: &[]string{utils.MetaLocalHost},
RequestPayload: utils.StringPointer(utils.MetaUrl),
ReplyPayload: utils.StringPointer(utils.MetaXml),
RequestProcessors: &[]*ReqProcessorJsnCfg{
{
ID: utils.StringPointer("OutboundAUTHDryRun"),
Filters: &[]string{"*string:*req.request_type:OutboundAUTH", "*string:*req.Msisdn:497700056231"},
@@ -142,12 +142,12 @@ func TestHttpAgentCfgsloadFromJsonCfgCase2(t *testing.T) {
}},
},
{
Id: utils.StringPointer("conecto_xml"),
Url: utils.StringPointer("/conecto_xml"),
Sessions_conns: &[]string{utils.MetaLocalHost},
Request_payload: utils.StringPointer("*xml"),
Reply_payload: utils.StringPointer("*xml"),
Request_processors: &[]*ReqProcessorJsnCfg{
ID: utils.StringPointer("conecto_xml"),
URL: utils.StringPointer("/conecto_xml"),
SessionSConns: &[]string{utils.MetaLocalHost},
RequestPayload: utils.StringPointer("*xml"),
ReplyPayload: utils.StringPointer("*xml"),
RequestProcessors: &[]*ReqProcessorJsnCfg{
{
ID: utils.StringPointer("cdr_from_xml"),
Tenant: utils.StringPointer("cgrates.org"),
@@ -230,12 +230,12 @@ func TestHttpAgentCfgsloadFromJsonCfgCase2(t *testing.T) {
func TestHttpAgentCfgloadFromJsonCfgCase3(t *testing.T) {
jsnhttpCfg := &HttpAgentJsonCfg{
Id: utils.StringPointer("conecto1"),
Url: utils.StringPointer("/conecto"),
Sessions_conns: &[]string{utils.MetaLocalHost},
Request_payload: utils.StringPointer("*url"),
Reply_payload: utils.StringPointer("*xml"),
Request_processors: &[]*ReqProcessorJsnCfg{
ID: utils.StringPointer("conecto1"),
URL: utils.StringPointer("/conecto"),
SessionSConns: &[]string{utils.MetaLocalHost},
RequestPayload: utils.StringPointer("*url"),
ReplyPayload: utils.StringPointer("*xml"),
RequestProcessors: &[]*ReqProcessorJsnCfg{
{
ID: utils.StringPointer("OutboundAUTHDryRun"),
Filters: &[]string{"*string:*req.request_type:OutboundAUTH", "*string:*req.Msisdn:497700056231"},
@@ -272,12 +272,12 @@ func TestHttpAgentCfgloadFromJsonCfgCase3(t *testing.T) {
func TestHttpAgentCfgloadFromJsonCfgCase4(t *testing.T) {
cfgJSON := &[]*HttpAgentJsonCfg{
{
Id: utils.StringPointer("conecto1"),
Url: utils.StringPointer("/conecto"),
Sessions_conns: &[]string{utils.MetaLocalHost},
Request_payload: utils.StringPointer(utils.MetaUrl),
Reply_payload: utils.StringPointer(utils.MetaXml),
Request_processors: &[]*ReqProcessorJsnCfg{
ID: utils.StringPointer("conecto1"),
URL: utils.StringPointer("/conecto"),
SessionSConns: &[]string{utils.MetaLocalHost},
RequestPayload: utils.StringPointer(utils.MetaUrl),
ReplyPayload: utils.StringPointer(utils.MetaXml),
RequestProcessors: &[]*ReqProcessorJsnCfg{
{
ID: utils.StringPointer("OutboundAUTHDryRun"),
Filters: &[]string{"*string:*req.request_type:OutboundAUTH", "*string:*req.Msisdn:497700056231"},
@@ -323,12 +323,12 @@ func TestHttpAgentCfgloadFromJsonCfgCase4(t *testing.T) {
}},
},
{
Id: utils.StringPointer("conecto_xml"),
Url: utils.StringPointer("/conecto_xml"),
Sessions_conns: &[]string{utils.MetaLocalHost},
Request_payload: utils.StringPointer("*xml"),
Reply_payload: utils.StringPointer("*xml"),
Request_processors: &[]*ReqProcessorJsnCfg{
ID: utils.StringPointer("conecto_xml"),
URL: utils.StringPointer("/conecto_xml"),
SessionSConns: &[]string{utils.MetaLocalHost},
RequestPayload: utils.StringPointer("*xml"),
ReplyPayload: utils.StringPointer("*xml"),
RequestProcessors: &[]*ReqProcessorJsnCfg{
{
ID: utils.StringPointer("cdr_from_xml"),
Tenant: utils.StringPointer("a{*"),
@@ -349,7 +349,7 @@ func TestHttpAgentCfgloadFromJsonCfgCase4(t *testing.T) {
func TestHttpAgentCfgloadFromJsonCfgCase5(t *testing.T) {
cfgJSON := &[]*HttpAgentJsonCfg{
{
Request_processors: nil,
RequestProcessors: nil,
},
}
jsonCfg := NewDefaultCGRConfig()
@@ -378,7 +378,7 @@ func TestHttpAgentCfgloadFromJsonCfgCase7(t *testing.T) {
}`
cfgJSON := &[]*HttpAgentJsonCfg{
{
Id: utils.StringPointer("RandomID"),
ID: utils.StringPointer("RandomID"),
},
}
expected := HTTPAgentCfgs{
@@ -498,6 +498,8 @@ func TestHttpAgentCfgAsMapInterface(t *testing.T) {
"id": "conecto1",
"url": "/conecto",
"sessions_conns": ["*birpc_internal", "*localhost","*internal"],
"stats_conns": ["*internal", "*localhost","*internal"],
"thresholds_conns": ["*internal", "*localhost","*internal"],
"request_payload": "*url",
"reply_payload": "*xml",
"request_processors": [
@@ -527,11 +529,13 @@ func TestHttpAgentCfgAsMapInterface(t *testing.T) {
}`
eMap := []map[string]any{
{
utils.IDCfg: "conecto1",
utils.URLCfg: "/conecto",
utils.SessionSConnsCfg: []string{rpcclient.BiRPCInternal, "*localhost", "*internal"},
utils.RequestPayloadCfg: "*url",
utils.ReplyPayloadCfg: "*xml",
utils.IDCfg: "conecto1",
utils.URLCfg: "/conecto",
utils.SessionSConnsCfg: []string{rpcclient.BiRPCInternal, "*localhost", "*internal"},
utils.StatSConnsCfg: []string{rpcclient.InternalRPC, "*localhost", "*internal"},
utils.ThresholdSConnsCfg: []string{rpcclient.InternalRPC, "*localhost", "*internal"},
utils.RequestPayloadCfg: "*url",
utils.ReplyPayloadCfg: "*xml",
utils.RequestProcessorsCfg: []map[string]any{
{
utils.IDCfg: "OutboundAUTHDryRun",

View File

@@ -593,12 +593,14 @@ type RadiusAgentJsonCfg struct {
// Conecto Agent configuration section
type HttpAgentJsonCfg struct {
Id *string
Url *string
Sessions_conns *[]string
Request_payload *string
Reply_payload *string
Request_processors *[]*ReqProcessorJsnCfg
ID *string `json:"id"`
URL *string `json:"url"`
SessionSConns *[]string `json:"sessions_conns"`
StatSConns *[]string `json:"stats_conns"`
ThresholdSConns *[]string `json:"thresholds_conns"`
RequestPayload *string `json:"request_payload"`
ReplyPayload *string `json:"reply_payload"`
RequestProcessors *[]*ReqProcessorJsnCfg `json:"request_processors"`
}
type DnsListenerJsnCfg struct {