/* Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments Copyright (C) ITsysCOM GmbH This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see */ package config import ( "fmt" "slices" "time" "github.com/cgrates/cgrates/utils" ) // NewFCTemplateFromFCTemplateJSONCfg creates a FCTemplate from json func NewFCTemplateFromFCTemplateJSONCfg(jsnCfg *FcTemplateJsonCfg, separator string) (*FCTemplate, error) { fcTmp := new(FCTemplate) var err error if jsnCfg.Type != nil { fcTmp.Type = *jsnCfg.Type } if jsnCfg.Path != nil { fcTmp.Path = *jsnCfg.Path fcTmp.pathSlice = utils.CompilePath(fcTmp.Path) fcTmp.Tag = fcTmp.Path } if jsnCfg.Tag != nil { fcTmp.Tag = *jsnCfg.Tag } if jsnCfg.Filters != nil { fcTmp.Filters = make([]string, len(*jsnCfg.Filters)) copy(fcTmp.Filters, *jsnCfg.Filters) } if jsnCfg.Value != nil { if fcTmp.Value, err = NewRSRParsers(*jsnCfg.Value, separator); err != nil { return nil, err } } if jsnCfg.Width != nil { fcTmp.Width = *jsnCfg.Width } if jsnCfg.Strip != nil { fcTmp.Strip = *jsnCfg.Strip } if jsnCfg.Padding != nil { fcTmp.Padding = *jsnCfg.Padding } if jsnCfg.Mandatory != nil { fcTmp.Mandatory = *jsnCfg.Mandatory } if jsnCfg.Attribute_id != nil { fcTmp.AttributeID = *jsnCfg.Attribute_id } if jsnCfg.New_branch != nil { fcTmp.NewBranch = *jsnCfg.New_branch } if jsnCfg.Timezone != nil { fcTmp.Timezone = *jsnCfg.Timezone } if jsnCfg.Blocker != nil { fcTmp.Blocker = *jsnCfg.Blocker } fcTmp.Layout = time.RFC3339 if jsnCfg.Layout != nil { fcTmp.Layout = *jsnCfg.Layout } if jsnCfg.Cost_shift_digits != nil { fcTmp.CostShiftDigits = *jsnCfg.Cost_shift_digits } if jsnCfg.Rounding_decimals != nil { fcTmp.RoundingDecimals = new(int) *fcTmp.RoundingDecimals = *jsnCfg.Rounding_decimals } if jsnCfg.Mask_destinationd_id != nil { fcTmp.MaskDestID = *jsnCfg.Mask_destinationd_id } if jsnCfg.Mask_length != nil { fcTmp.MaskLen = *jsnCfg.Mask_length } return fcTmp, nil } // FCTemplate the teplate for a field type FCTemplate struct { Tag string Type string // Type of field Path string // Field identifier Filters []string // list of filter profiles Value RSRParsers Width int Strip string Padding string Mandatory bool AttributeID string // Used by NavigableMap when creating CGREvent/XMLElements NewBranch bool // Used by NavigableMap when creating XMLElements Timezone string Blocker bool Layout string // time format CostShiftDigits int // Used for CDR RoundingDecimals *int MaskDestID string MaskLen int pathSlice []string // Field identifier } // FCTemplatesFromFCTemplatesJSONCfg will build a list of FCTemplates from json func FCTemplatesFromFCTemplatesJSONCfg(jsnCfgFlds []*FcTemplateJsonCfg, separator string) (retFields []*FCTemplate, err error) { retFields = make([]*FCTemplate, len(jsnCfgFlds)) for i, jsnFld := range jsnCfgFlds { if retFields[i], err = NewFCTemplateFromFCTemplateJSONCfg(jsnFld, separator); err != nil { return nil, err } } return } // InflateTemplates will replace the *template fields with template content out msgTpls func InflateTemplates(fcts []*FCTemplate, msgTpls map[string][]*FCTemplate) ([]*FCTemplate, error) { var hasTpl bool for i := 0; i < len(fcts); { if fcts[i].Type == utils.MetaTemplate { hasTpl = true tplID, err := fcts[i].Value.ParseValue(nil) if err != nil { return nil, err } refTpl, has := msgTpls[tplID] if !has { return nil, fmt.Errorf("no template with id: <%s>", tplID) } else if len(refTpl) == 0 { return nil, fmt.Errorf("empty template with id: <%s>", tplID) } wrkSlice := make([]*FCTemplate, len(refTpl)+len(fcts[i:])-1) // so we can cover tpls[i+1:] copy(wrkSlice[:len(refTpl)], refTpl) // copy fields out of referenced template if len(fcts[i:]) > 1 { // copy the rest of the fields after MetaTemplate copy(wrkSlice[len(refTpl):], fcts[i+1:]) } fcts = append(fcts[:i], wrkSlice...) // append the work continue // don't increase index so we can recheck } i++ } if !hasTpl { return nil, nil } return fcts, nil } // Clone returns a deep copy of FCTemplate func (fc FCTemplate) Clone() (cln *FCTemplate) { cln = &FCTemplate{ Tag: fc.Tag, Type: fc.Type, Path: fc.Path, Value: fc.Value.Clone(), Width: fc.Width, Strip: fc.Strip, Padding: fc.Padding, Mandatory: fc.Mandatory, AttributeID: fc.AttributeID, NewBranch: fc.NewBranch, Timezone: fc.Timezone, Blocker: fc.Blocker, Layout: fc.Layout, CostShiftDigits: fc.CostShiftDigits, MaskDestID: fc.MaskDestID, MaskLen: fc.MaskLen, } if fc.RoundingDecimals != nil { cln.RoundingDecimals = new(int) *cln.RoundingDecimals = *fc.RoundingDecimals } if fc.pathSlice != nil { cln.pathSlice = slices.Clone(fc.pathSlice) } if fc.Filters != nil { cln.Filters = slices.Clone(fc.Filters) } return } // FcTemplates the config for the templates type FcTemplates map[string][]*FCTemplate // AsMapInterface returns the config as a map[string]any func (sCft FcTemplates) AsMapInterface(separator string) (initialMP map[string][]map[string]any) { initialMP = make(map[string][]map[string]any) for key, value := range sCft { initialMP[key] = make([]map[string]any, len(value)) for i, item := range value { initialMP[key][i] = item.AsMapInterface(separator) } } return } // AsMapInterface returns the config as a map[string]any func (fc *FCTemplate) AsMapInterface(separator string) (mp map[string]any) { mp = make(map[string]any) if fc.Tag != utils.EmptyString { mp[utils.TagCfg] = fc.Tag } if fc.Type != utils.EmptyString { mp[utils.TypeCf] = fc.Type } if fc.Path != utils.EmptyString { mp[utils.PathCfg] = fc.Path } if fc.Filters != nil { mp[utils.FiltersCfg] = fc.Filters } if fc.Value != nil { mp[utils.ValueCfg] = fc.Value.GetRule(separator) } if fc.Width != 0 { mp[utils.WidthCfg] = fc.Width } if fc.Strip != utils.EmptyString { mp[utils.StripCfg] = fc.Strip } if fc.Padding != utils.EmptyString { mp[utils.PaddingCfg] = fc.Padding } if fc.Mandatory { mp[utils.MandatoryCfg] = fc.Mandatory } if fc.AttributeID != utils.EmptyString { mp[utils.AttributeIDCfg] = fc.AttributeID } if fc.NewBranch { mp[utils.NewBranchCfg] = fc.NewBranch } if fc.Timezone != utils.EmptyString { mp[utils.TimezoneCfg] = fc.Timezone } if fc.Blocker { mp[utils.BlockerCfg] = fc.Blocker } if fc.Layout != time.RFC3339 { mp[utils.LayoutCfg] = fc.Layout } if fc.CostShiftDigits != 0 { mp[utils.CostShiftDigitsCfg] = fc.CostShiftDigits } if fc.RoundingDecimals != nil { mp[utils.RoundingDecimalsCfg] = *fc.RoundingDecimals } if fc.MaskDestID != utils.EmptyString { mp[utils.MaskDestIDCfg] = fc.MaskDestID } if fc.MaskLen != 0 { mp[utils.MaskLenCfg] = fc.MaskLen } return } // GetPathSlice returns the cached split of the path func (fc *FCTemplate) GetPathSlice() []string { return fc.pathSlice } // ComputePath used in test to populate private fields used to store the path func (fc *FCTemplate) ComputePath() { fc.pathSlice = utils.CompilePath(fc.Path) } // Clone returns a deep copy of FcTemplates func (sCft FcTemplates) Clone() (cln FcTemplates) { if sCft == nil { return } cln = make(FcTemplates) for k, fcs := range sCft { fcln := make([]*FCTemplate, len(fcs)) for i, fc := range fcs { fcln[i] = fc.Clone() } cln[k] = fcln } return }