From 61b823443da195212c26ad385c34c0e25cf90e2c Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 18 Nov 2021 10:19:30 +0200 Subject: [PATCH] Finished adding all fieldAsInterface methods --- engine/account.go | 32 +++++- engine/balance_filter.go | 210 ++++++++++++++++++++++++++++++++++++++ engine/balances.go | 213 +++++++++++++++++++++++++++++++++++++++ engine/rateinterval.go | 144 ++++++++++++++++++++++++-- engine/units_counter.go | 69 ++++++++++++- utils/consts.go | 8 ++ utils/map.go | 20 ++++ utils/value_formula.go | 39 +++++++ 8 files changed, 719 insertions(+), 16 deletions(-) diff --git a/engine/account.go b/engine/account.go index bb5b6d2a5..5da6c027e 100644 --- a/engine/account.go +++ b/engine/account.go @@ -1325,10 +1325,21 @@ func (acc *Account) FieldAsInterface(fldPath []string) (val interface{}, err err } return acc.ID, nil case utils.BalanceMap: - if len(fldPath) != 1 { + if len(fldPath) == 1 { return acc.BalanceMap, nil } - if bc, has := acc.BalanceMap[fldPath[1]]; has { + opath, indx := utils.GetPathIndex(fldPath[1]) + if bc, has := acc.BalanceMap[opath]; has { + if indx != nil { + if len(bc) <= *indx { + return nil, utils.ErrNotFound + } + c := bc[*indx] + if len(fldPath) == 2 { + return c, nil + } + return c.FieldAsInterface(fldPath[2:]) + } if len(fldPath) == 2 { return bc, nil } @@ -1336,18 +1347,29 @@ func (acc *Account) FieldAsInterface(fldPath []string) (val interface{}, err err } return nil, utils.ErrNotFound case utils.UnitCounters: - if len(fldPath) != 1 { + if len(fldPath) == 1 { return acc.UnitCounters, nil } if uc, has := acc.UnitCounters[fldPath[1]]; has { if len(fldPath) == 2 { return uc, nil } - return uc.FieldAsInterface(fldPath[2:]) + var indx int + if indx, err = strconv.Atoi(fldPath[2]); err != nil { + return + } + if len(uc) <= indx { + return nil, utils.ErrNotFound + } + c := uc[indx] + if len(fldPath) == 1 { + return c, nil + } + return c.FieldAsInterface(fldPath[3:]) } return nil, utils.ErrNotFound case utils.ActionTriggers: - if len(fldPath) != 1 { + if len(fldPath) == 1 { return acc.ActionTriggers, nil } for _, at := range acc.ActionTriggers { diff --git a/engine/balance_filter.go b/engine/balance_filter.go index d554922e6..80b9109e4 100644 --- a/engine/balance_filter.go +++ b/engine/balance_filter.go @@ -19,8 +19,10 @@ along with this program. If not, see package engine import ( + "fmt" "math" "reflect" + "strconv" "time" "github.com/cgrates/cgrates/utils" @@ -417,3 +419,211 @@ func (bf *BalanceFilter) ModifyBalance(b *Balance) { } b.SetDirty() // Mark the balance as dirty since we have modified and it should be checked by action triggers } + +func (bp *BalanceFilter) String() string { + return utils.ToJSON(bp) +} + +func (bp *BalanceFilter) FieldAsInterface(fldPath []string) (val interface{}, err error) { + if bp == nil || len(fldPath) == 0 { + return nil, utils.ErrNotFound + } + switch fldPath[0] { + default: + opath, indx := utils.GetPathIndexString(fldPath[0]) + if indx != nil { + switch opath { + case utils.DestinationIDs: + if bp.DestinationIDs == nil { + return nil, utils.ErrNotFound + } + val, has := (*bp.DestinationIDs)[*indx] + if !has || len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + return val, nil + case utils.Categories: + if bp.Categories == nil { + return nil, utils.ErrNotFound + } + val, has := (*bp.Categories)[*indx] + if !has || len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + return val, nil + case utils.SharedGroups: + if bp.SharedGroups == nil { + return nil, utils.ErrNotFound + } + val, has := (*bp.SharedGroups)[*indx] + if !has || len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + return val, nil + case utils.TimingIDs: + if bp.TimingIDs == nil { + return nil, utils.ErrNotFound + } + val, has := (*bp.TimingIDs)[*indx] + if !has || len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + return val, nil + case utils.Timings: + var idx int + if idx, err = strconv.Atoi(*indx); err != nil { + return + } + if len(bp.Timings) <= idx { + return nil, utils.ErrNotFound + } + tm := bp.Timings[idx] + if len(fldPath) == 1 { + return tm, nil + } + return tm.FieldAsInterface(fldPath[1:]) + case utils.Factor: + if bp.Factor == nil { + return nil, utils.ErrNotFound + } + val, has := (*bp.Factor)[*indx] + if !has || len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + return val, nil + } + } + return nil, fmt.Errorf("unsupported field prefix: <%s>", fldPath[0]) + case utils.Uuid: + if len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + if bp.Uuid == nil { + return + } + return *bp.Uuid, nil + case utils.ID: + if len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + if bp.ID == nil { + return + } + return *bp.ID, nil + case utils.Type: + if len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + if bp.Type == nil { + return + } + return *bp.Type, nil + case utils.ExpirationDate: + if len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + if bp.ExpirationDate == nil { + return + } + return *bp.ExpirationDate, nil + case utils.Weight: + if len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + if bp.Weight == nil { + return + } + return *bp.Weight, nil + case utils.RatingSubject: + if len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + if bp.RatingSubject == nil { + return + } + return *bp.RatingSubject, nil + case utils.Disabled: + if len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + if bp.Disabled == nil { + return + } + return *bp.Disabled, nil + case utils.Blocker: + if len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + if bp.Blocker == nil { + return + } + return *bp.Blocker, nil + case utils.DestinationIDs: + if len(fldPath) == 1 { + return bp.DestinationIDs, nil + } + if bp.DestinationIDs == nil { + return nil, utils.ErrNotFound + } + return bp.DestinationIDs.FieldAsInterface(fldPath[1:]) + case utils.Categories: + if len(fldPath) == 1 { + return bp.Categories, nil + } + if bp.Categories == nil { + return nil, utils.ErrNotFound + } + return bp.Categories.FieldAsInterface(fldPath[1:]) + case utils.SharedGroups: + if len(fldPath) == 1 { + return bp.SharedGroups, nil + } + if bp.SharedGroups == nil { + return nil, utils.ErrNotFound + } + return bp.SharedGroups.FieldAsInterface(fldPath[1:]) + case utils.Timings: + if len(fldPath) == 1 { + return bp.Timings, nil + } + for _, tm := range bp.Timings { + if tm.ID == fldPath[1] { + if len(fldPath) == 2 { + return tm, nil + } + return tm.FieldAsInterface(fldPath[2:]) + } + } + return nil, utils.ErrNotFound + case utils.TimingIDs: + if len(fldPath) == 1 { + return bp.TimingIDs, nil + } + if bp.TimingIDs == nil { + return nil, utils.ErrNotFound + } + return bp.TimingIDs.FieldAsInterface(fldPath[1:]) + case utils.Factor: + if len(fldPath) == 1 { + return bp.Factor, nil + } + if bp.Factor == nil { + return nil, utils.ErrNotFound + } + return bp.Factor.FieldAsInterface(fldPath[1:]) + case utils.Value: + if len(fldPath) == 1 { + return bp.Value, nil + } + return bp.Value.FieldAsInterface(fldPath[1:]) + } +} + +func (bp *BalanceFilter) FieldAsString(fldPath []string) (val string, err error) { + var iface interface{} + iface, err = bp.FieldAsInterface(fldPath) + if err != nil { + return + } + return utils.IfaceAsString(iface), nil +} diff --git a/engine/balances.go b/engine/balances.go index 7d9d1cd95..fb0d1fa93 100644 --- a/engine/balances.go +++ b/engine/balances.go @@ -22,6 +22,7 @@ import ( "errors" "fmt" "sort" + "strconv" "strings" "time" @@ -1097,3 +1098,215 @@ func (b *Balance) debit(cd *CallDescriptor, ub *Account, moneyBalances Balances, } return } + +func (bc Balances) String() string { + return utils.ToJSON(bc) +} + +func (bc Balances) FieldAsInterface(fldPath []string) (val interface{}, err error) { + if bc == nil || len(fldPath) == 0 { + return nil, utils.ErrNotFound + } + for _, at := range bc { + if at.ID == fldPath[0] { + if len(fldPath) == 1 { + return at, nil + } + return at.FieldAsInterface(fldPath[1:]) + } + } + var indx int + if indx, err = strconv.Atoi(fldPath[0]); err != nil { + return + } + if len(bc) <= indx { + return nil, utils.ErrNotFound + } + c := bc[indx] + if len(fldPath) == 1 { + return c, nil + } + return c.FieldAsInterface(fldPath[1:]) +} + +func (bc Balances) FieldAsString(fldPath []string) (val string, err error) { + var iface interface{} + iface, err = bc.FieldAsInterface(fldPath) + if err != nil { + return + } + return utils.IfaceAsString(iface), nil +} + +func (b *Balance) String() string { + return utils.ToJSON(b) +} + +func (b *Balance) FieldAsInterface(fldPath []string) (val interface{}, err error) { + if b == nil || len(fldPath) == 0 { + return nil, utils.ErrNotFound + } + switch fldPath[0] { + default: + opath, indx := utils.GetPathIndexString(fldPath[0]) + if indx != nil { + switch opath { + case utils.DestinationIDs: + val, has := b.DestinationIDs[*indx] + if !has || len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + return val, nil + case utils.Categories: + val, has := b.Categories[*indx] + if !has || len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + return val, nil + case utils.SharedGroups: + val, has := b.SharedGroups[*indx] + if !has || len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + return val, nil + case utils.TimingIDs: + val, has := b.TimingIDs[*indx] + if !has || len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + return val, nil + case utils.Timings: + var idx int + if idx, err = strconv.Atoi(*indx); err != nil { + return + } + if len(b.Timings) <= idx { + return nil, utils.ErrNotFound + } + tm := b.Timings[idx] + if len(fldPath) == 1 { + return tm, nil + } + return tm.FieldAsInterface(fldPath[1:]) + case utils.Factor: + val, has := b.Factor[*indx] + if !has || len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + return val, nil + } + } + return nil, fmt.Errorf("unsupported field prefix: <%s>", fldPath[0]) + case utils.Uuid: + if len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + return b.Uuid, nil + case utils.ID: + if len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + return b.ID, nil + case utils.Value: + if len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + return b.Value, nil + case utils.ExpirationDate: + if len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + return b.ExpirationDate, nil + case utils.Weight: + if len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + return b.Weight, nil + case utils.DestinationIDs: + if len(fldPath) == 1 { + return b.DestinationIDs, nil + } + return b.DestinationIDs.FieldAsInterface(fldPath[1:]) + case utils.RatingSubject: + if len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + return b.RatingSubject, nil + case utils.Categories: + if len(fldPath) == 1 { + return b.Categories, nil + } + return b.Categories.FieldAsInterface(fldPath[1:]) + case utils.SharedGroups: + if len(fldPath) == 1 { + return b.SharedGroups, nil + } + return b.SharedGroups.FieldAsInterface(fldPath[1:]) + case utils.Timings: + if len(fldPath) == 1 { + return b.Timings, nil + } + for _, tm := range b.Timings { + if tm.ID == fldPath[1] { + if len(fldPath) == 2 { + return tm, nil + } + return tm.FieldAsInterface(fldPath[2:]) + } + } + return nil, utils.ErrNotFound + case utils.TimingIDs: + if len(fldPath) == 1 { + return b.TimingIDs, nil + } + return b.TimingIDs.FieldAsInterface(fldPath[1:]) + case utils.Disabled: + if len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + return b.Disabled, nil + case utils.Factor: + if len(fldPath) == 1 { + return b.Factor, nil + } + return b.Factor.FieldAsInterface(fldPath[1:]) + case utils.Blocker: + if len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + return b.Blocker, nil + } +} + +func (b *Balance) FieldAsString(fldPath []string) (val string, err error) { + var iface interface{} + iface, err = b.FieldAsInterface(fldPath) + if err != nil { + return + } + return utils.IfaceAsString(iface), nil +} + +func (f ValueFactor) String() string { + return utils.ToJSON(f) +} + +func (f ValueFactor) FieldAsInterface(fldPath []string) (val interface{}, err error) { + if f == nil || len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + c, has := f[fldPath[0]] + if !has { + return nil, utils.ErrNotFound + } + return c, nil +} + +func (f ValueFactor) FieldAsString(fldPath []string) (val string, err error) { + var iface interface{} + iface, err = f.FieldAsInterface(fldPath) + if err != nil { + return + } + return utils.IfaceAsString(iface), nil +} diff --git a/engine/rateinterval.go b/engine/rateinterval.go index 1c5b35871..5c30a5a2e 100644 --- a/engine/rateinterval.go +++ b/engine/rateinterval.go @@ -40,14 +40,15 @@ type RateInterval struct { // Separate structure used for rating plan size optimization type RITiming struct { - ID string - Years utils.Years - Months utils.Months - MonthDays utils.MonthDays - WeekDays utils.WeekDays - StartTime, EndTime string // ##:##:## format - cronString string - tag string // loading validation only + ID string + Years utils.Years + Months utils.Months + MonthDays utils.MonthDays + WeekDays utils.WeekDays + StartTime string // ##:##:## format + EndTime string // ##:##:## format + cronString string + tag string // loading validation only } func (rit *RITiming) CronString() string { @@ -527,3 +528,130 @@ func (r *RGRate) Clone() (cln *RGRate) { } return } + +func (rit *RITiming) String() string { + return utils.ToJSON(rit) +} + +func (rit *RITiming) FieldAsInterface(fldPath []string) (val interface{}, err error) { + if rit == nil || len(fldPath) == 0 { + return nil, utils.ErrNotFound + } + switch fldPath[0] { + default: + opath, indx := utils.GetPathIndex(fldPath[0]) + if indx != nil { + switch opath { + case utils.YearsFieldName: + if len(fldPath) != 1 || len(rit.Years) <= *indx { + return nil, utils.ErrNotFound + } + return rit.Years[*indx], nil + case utils.MonthsFieldName: + if len(fldPath) != 1 || len(rit.Months) <= *indx { + return nil, utils.ErrNotFound + } + return rit.Months[*indx], nil + case utils.MonthDaysFieldName: + if len(fldPath) != 1 || len(rit.MonthDays) <= *indx { + return nil, utils.ErrNotFound + } + return rit.MonthDays[*indx], nil + case utils.WeekDaysFieldName: + if len(fldPath) != 1 || len(rit.WeekDays) <= *indx { + return nil, utils.ErrNotFound + } + return rit.WeekDays[*indx], nil + } + } + return nil, fmt.Errorf("unsupported field prefix: <%s>", fldPath[0]) + case utils.ID: + if len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + return rit.ID, nil + case utils.StartTime: + if len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + return rit.StartTime, nil + case utils.EndTime: + if len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + return rit.EndTime, nil + case utils.YearsFieldName: + switch len(fldPath) { + case 1: + return rit.Years, nil + case 2: + var idx int + if idx, err = strconv.Atoi(fldPath[1]); err != nil { + return + } + if len(rit.Years) <= idx { + return nil, utils.ErrNotFound + } + return rit.Years[idx], nil + default: + return nil, utils.ErrNotFound + } + case utils.MonthsFieldName: + switch len(fldPath) { + case 1: + return rit.Months, nil + case 2: + var idx int + if idx, err = strconv.Atoi(fldPath[1]); err != nil { + return + } + if len(rit.Months) <= idx { + return nil, utils.ErrNotFound + } + return rit.Months[idx], nil + default: + return nil, utils.ErrNotFound + } + case utils.MonthDaysFieldName: + switch len(fldPath) { + case 1: + return rit.MonthDays, nil + case 2: + var idx int + if idx, err = strconv.Atoi(fldPath[1]); err != nil { + return + } + if len(rit.MonthDays) <= idx { + return nil, utils.ErrNotFound + } + return rit.MonthDays[idx], nil + default: + return nil, utils.ErrNotFound + } + case utils.WeekDaysFieldName: + switch len(fldPath) { + case 1: + return rit.WeekDays, nil + case 2: + var idx int + if idx, err = strconv.Atoi(fldPath[1]); err != nil { + return + } + if len(rit.WeekDays) <= idx { + return nil, utils.ErrNotFound + } + return rit.WeekDays[idx], nil + default: + return nil, utils.ErrNotFound + } + } +} + +func (rit *RITiming) FieldAsString(fldPath []string) (val string, err error) { + var iface interface{} + iface, err = rit.FieldAsInterface(fldPath) + if err != nil { + return + } + return utils.IfaceAsString(iface), nil +} diff --git a/engine/units_counter.go b/engine/units_counter.go index 0012da03c..40cee73b9 100644 --- a/engine/units_counter.go +++ b/engine/units_counter.go @@ -19,6 +19,9 @@ along with this program. If not, see package engine import ( + "fmt" + "strconv" + "github.com/cgrates/cgrates/utils" ) @@ -152,7 +155,6 @@ func (ucs UnitCounters) resetCounters(a *Action) { } } -/* func (uc *UnitCounter) String() string { return utils.ToJSON(uc) } @@ -163,10 +165,39 @@ func (uc *UnitCounter) FieldAsInterface(fldPath []string) (val interface{}, err } switch fldPath[0] { default: + opath, indx := utils.GetPathIndex(fldPath[0]) + if opath == utils.Counters && indx != nil { + if len(uc.Counters) <= *indx { + return nil, utils.ErrNotFound + } + c := uc.Counters[*indx] + if len(fldPath) == 1 { + return c, nil + } + return c.FieldAsInterface(fldPath[1:]) + } return nil, fmt.Errorf("unsupported field prefix: <%s>", fldPath[0]) case utils.CounterType: + if len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + return uc.CounterType, nil case utils.Counters: - + if len(fldPath) == 1 { + return uc.Counters, nil + } + var indx int + if indx, err = strconv.Atoi(fldPath[1]); err != nil { + return + } + if len(uc.Counters) <= indx { + return nil, utils.ErrNotFound + } + c := uc.Counters[indx] + if len(fldPath) == 2 { + return c, nil + } + return c.FieldAsInterface(fldPath[2:]) } } @@ -178,4 +209,36 @@ func (uc *UnitCounter) FieldAsString(fldPath []string) (val string, err error) { } return utils.IfaceAsString(iface), nil } -*/ + +func (cfs *CounterFilter) String() string { + return utils.ToJSON(cfs) +} + +func (cfs *CounterFilter) FieldAsInterface(fldPath []string) (val interface{}, err error) { + if cfs == nil || len(fldPath) == 0 { + return nil, utils.ErrNotFound + } + switch fldPath[0] { + default: + return nil, fmt.Errorf("unsupported field prefix: <%s>", fldPath[0]) + case utils.Value: + if len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + return cfs.Value, nil + case utils.Filter: + if len(fldPath) == 1 { + return cfs.Filter, nil + } + return cfs.Filter.FieldAsInterface(fldPath[1:]) + } +} + +func (cfs *CounterFilter) FieldAsString(fldPath []string) (val string, err error) { + var iface interface{} + iface, err = cfs.FieldAsInterface(fldPath) + if err != nil { + return + } + return utils.IfaceAsString(iface), nil +} diff --git a/utils/consts.go b/utils/consts.go index bd96d68d9..1c5f2c693 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -228,6 +228,7 @@ const ( CustomValue = "CustomValue" Value = "Value" + Filter = "Filter" LastUsed = "LastUsed" PDD = "PDD" Route = "Route" @@ -435,6 +436,8 @@ const ( ActionPlans = "ActionPlans" ActionTriggers = "ActionTriggers" BalanceMap = "BalanceMap" + CounterType = "CounterType" + Counters = "Counters" UnitCounters = "UnitCounters" UpdateTime = "UpdateTime" SharedGroups = "SharedGroups" @@ -544,6 +547,7 @@ const ( Blocker = "Blocker" RatingPlanID = "RatingPlanID" StartTime = "StartTime" + EndTime = "EndTime" AccountSummary = "AccountSummary" RatingFilters = "RatingFilters" RatingFilter = "RatingFilter" @@ -742,6 +746,7 @@ const ( Load = "Load" Slash = "/" UUID = "UUID" + Uuid = "Uuid" ActionsID = "ActionsID" MetaAct = "*act" MetaAcnt = "*acnt" @@ -850,6 +855,9 @@ const ( UnitFactors = "UnitFactors" CostIncrements = "CostIncrements" Factor = "Factor" + Method = "Method" + Static = "Static" + Params = "Params" Increment = "Increment" FixedFee = "FixedFee" RecurrentFee = "RecurrentFee" diff --git a/utils/map.go b/utils/map.go index 97aeea0e4..b7abb2393 100644 --- a/utils/map.go +++ b/utils/map.go @@ -289,3 +289,23 @@ func (fWp FlagsWithParams) Clone() (cln FlagsWithParams) { } return } + +func (sm StringMap) FieldAsInterface(fldPath []string) (val interface{}, err error) { + if sm == nil || len(fldPath) != 1 { + return nil, ErrNotFound + } + bl, has := sm[fldPath[0]] + if !has { + return nil, ErrNotFound + } + return bl, nil +} + +func (sm StringMap) FieldAsString(fldPath []string) (val string, err error) { + var iface interface{} + iface, err = sm.FieldAsInterface(fldPath) + if err != nil { + return + } + return IfaceAsString(iface), nil +} diff --git a/utils/value_formula.go b/utils/value_formula.go index 6a236c765..67020005f 100644 --- a/utils/value_formula.go +++ b/utils/value_formula.go @@ -21,6 +21,7 @@ package utils import ( "encoding/json" "errors" + "fmt" "strconv" "time" ) @@ -122,3 +123,41 @@ func incrementalFormula(params map[string]interface{}) float64 { } return 0.0 } + +func (vf *ValueFormula) FieldAsInterface(fldPath []string) (val interface{}, err error) { + if vf == nil || len(fldPath) == 0 { + return nil, ErrNotFound + } + switch fldPath[0] { + default: + opath, indx := GetPathIndexString(fldPath[0]) + if indx != nil && opath == Params { + return MapStorage(vf.Params).FieldAsInterface(append([]string{*indx}, fldPath[1:]...)) + } + return nil, fmt.Errorf("unsupported field prefix: <%s>", fldPath[0]) + case Method: + if len(fldPath) != 1 { + return nil, ErrNotFound + } + return vf.Method, nil + case Static: + if len(fldPath) != 1 { + return nil, ErrNotFound + } + return vf.Static, nil + case Params: + if len(fldPath) == 1 { + return vf.Params, nil + } + return MapStorage(vf.Params).FieldAsInterface(fldPath[1:]) + } +} + +func (vf *ValueFormula) FieldAsString(fldPath []string) (val string, err error) { + var iface interface{} + iface, err = vf.FieldAsInterface(fldPath) + if err != nil { + return + } + return IfaceAsString(iface), nil +}