From 0bc94857ca9992dc793df208fbc7bc23eeec052a Mon Sep 17 00:00:00 2001 From: ionutboangiu Date: Fri, 13 Oct 2023 10:38:37 -0400 Subject: [PATCH] Update empty xml_root_path sanity check case. Also added tests for it. Added comment to the *HierarchyPath.AsString() function, addressing the issue of not being able to retrieve the element when the root path is equal to the path that is being processed. --- config/configsanity.go | 19 +++++++------ config/configsanity_test.go | 55 +++++++++++++++++++++++++++++++++++++ utils/coreutils.go | 6 ++++ 3 files changed, 72 insertions(+), 8 deletions(-) diff --git a/config/configsanity.go b/config/configsanity.go index b71e625c1..deb7ca227 100644 --- a/config/configsanity.go +++ b/config/configsanity.go @@ -515,21 +515,24 @@ func (cfg *CGRConfig) checkConfigSanity() error { } // The following sanity check prevents a "slice bounds out of range" panic. - if rdr.Type == utils.MetaFileXML && !utils.IsSliceMember([]string{utils.META_NONE, utils.META_CONSTANT, - utils.META_FILLER, utils.MetaRemoteHost}, field.Type) && len(field.Value) != 0 { + if rdr.Type == utils.MetaFileXML && len(field.Value) != 0 && + !utils.IsSliceMember([]string{utils.META_NONE, utils.META_CONSTANT}, field.Type) { - // Retrieve the number of elements of the parser rule with the fewest elements. + // Find the minimum rule length for dynamic RSRParser within the field value. minRuleLength := math.MaxInt - for _, rule := range field.Value { - if ruleLen := len(strings.Split(rule.AttrName(), utils.NestingSep)); minRuleLength > ruleLen { - minRuleLength = ruleLen + for _, parser := range field.Value { + if !strings.HasPrefix(parser.Rules, utils.DynamicDataPrefix) { + continue } + ruleLen := len(strings.Split(parser.Rules, utils.NestingSep)) + minRuleLength = min(minRuleLength, ruleLen) } - if len(rdr.XmlRootPath) >= minRuleLength { + // If a dynamic RSRParser is found, verify xml_root_path length against minRuleLength. + if minRuleLength != math.MaxInt && len(rdr.XmlRootPath) >= minRuleLength { return fmt.Errorf("<%s> %s for reader %s at %s", utils.ERs, - "len of xml_root_path elements cannot be equal to or exceed the number of value rule elements", + "xml_root_path length exceeds value rule elements", rdr.ID, field.Tag) } } diff --git a/config/configsanity_test.go b/config/configsanity_test.go index 166598e48..7018a27bc 100644 --- a/config/configsanity_test.go +++ b/config/configsanity_test.go @@ -873,3 +873,58 @@ func TestConfigSanityFilterS(t *testing.T) { cfg.filterSCfg.ResourceSConns = []string{} } + +func TestConfigSanityERsXmlRootPath(t *testing.T) { + cfg, err := NewCGRConfigFromJsonStringWithDefaults(`{ +"ers": { + "enabled": true, + "sessions_conns": [], + "readers": [ + { + "id": "*default", + "type": "*file_xml", + "source_path": "/tmp", + "processed_path": "/tmp", + "flags": ["*dryrun"], + "xml_root_path": "A.B", + "fields":[ + {"tag": "VariableFld", "path": "*cgreq.VariableFld", "type": "*variable", "value": "~*req.A.B.Value1", "mandatory": true}, + {"tag": "ComposedFld", "path": "*cgreq.ComposedFld", "type": "*composed", "value": "~*req.A.B.Value2", "mandatory": true}, + {"tag": "ComposedFld", "path": "*cgreq.ComposedFld", "type": "*composed", "value": "_", "mandatory": true}, + {"tag": "ComposedFld", "path": "*cgreq.ComposedFld", "type": "*composed", "value": "~*req.A.B.Value3", "mandatory": true}, + {"tag": "ConstantFld", "path": "*cgreq.ConstantFld", "type": "*constant", "value": "Value4", "mandatory": true}, + {"tag": "UsageDiffFld", "path": "*cgreq.UsageDiffFld", "type": "*usage_difference", "value": "~*req.A.B.Value5;~*req.A.B.Value6", "mandatory": true}, + {"tag": "NoneFld", "path": "*cgreq.NoneFld", "type": "*none", "mandatory": true}, + {"tag": "FillerFld", "path": "*cgreq.FillerFld", "type": "*filler", "width": 5, "mandatory": true} + ] + } + ] +}}`) + if err != nil { + t.Fatal(err) + } + + err = cfg.checkConfigSanity() + if err != nil { + t.Fatal(err) + } + + cfg.ERsCfg().Readers[0].XmlRootPath = utils.ParseHierarchyPath("", utils.EmptyString) + err = cfg.checkConfigSanity() + if err != nil { + t.Fatal(err) + } + + cfg.ERsCfg().Readers[0].XmlRootPath = utils.ParseHierarchyPath("A.B.Value1", utils.EmptyString) + err = cfg.checkConfigSanity() + if err != nil { + t.Fatal(err) + } + + experr := ` xml_root_path length exceeds value rule elements for reader *default at VariableFld` + cfg.ERsCfg().Readers[0].XmlRootPath = utils.ParseHierarchyPath("A.B.C.D", utils.EmptyString) + err = cfg.checkConfigSanity() + if err == nil || err.Error() != experr { + t.Errorf("expected: %s, received: %s", experr, err) + } +} diff --git a/utils/coreutils.go b/utils/coreutils.go index ffb6b2d3b..ca46c6a43 100644 --- a/utils/coreutils.go +++ b/utils/coreutils.go @@ -650,6 +650,12 @@ func (hP HierarchyPath) AsString(sep string, prefix bool) string { if prefix { strHP.WriteString(sep) } + + // TODO: Since this function can convert both the full and the relative HierarchyPath to a string, with + // prefix telling us which is which (true -> full path, false -> relative path), we should consider + // returning the '.' character when prefix == false. Currently, because we are returning an empty string + // if the path we are currently parsing is equal to the root path, when we attempt to retrieve the element + // we receive the error "expr expression is nil". if len(hP) == 0 { return strHP.String() }