diff --git a/config/config_test.go b/config/config_test.go index 113f62832..f97de9de3 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -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) diff --git a/config/configsanity.go b/config/configsanity.go index e2a70c073..59a1acc94 100644 --- a/config/configsanity.go +++ b/config/configsanity.go @@ -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) } diff --git a/config/httpagntcfg.go b/config/httpagent.go similarity index 72% rename from config/httpagntcfg.go rename to config/httpagent.go index e0b1ea116..5127b2236 100644 --- a/config/httpagntcfg.go +++ b/config/httpagent.go @@ -19,6 +19,8 @@ along with this program. If not, see 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 } diff --git a/config/httpagntcfg_test.go b/config/httpagent_test.go similarity index 89% rename from config/httpagntcfg_test.go rename to config/httpagent_test.go index 45a0a1184..606f10586 100644 --- a/config/httpagntcfg_test.go +++ b/config/httpagent_test.go @@ -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", diff --git a/config/libconfig_json.go b/config/libconfig_json.go index dbe83578a..413146f26 100644 --- a/config/libconfig_json.go +++ b/config/libconfig_json.go @@ -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 {