diff --git a/apier/v1/apier_it_test.go b/apier/v1/apier_it_test.go
index e02ffbcb1..65782d8e5 100644
--- a/apier/v1/apier_it_test.go
+++ b/apier/v1/apier_it_test.go
@@ -1119,7 +1119,14 @@ func TestApierSetAccountActionTriggers(t *testing.T) {
t.Errorf("Unexpected action triggers received %v", reply)
}
var setReply string
- setReq := AttrSetAccountActionTriggers{Tenant: "cgrates.org", Account: "dan2", UniqueID: reply[0].UniqueID, ActivationDate: utils.StringPointer("2016-02-05T18:00:00Z")}
+ setReq := AttrSetAccountActionTriggers{
+ Tenant: "cgrates.org",
+ Account: "dan2",
+ UniqueID: reply[0].UniqueID,
+ ActionTrigger: map[string]interface{}{
+ utils.ActivationDate: "2016-02-05T18:00:00Z",
+ },
+ }
if err := rater.Call(utils.ApierV1ResetAccountActionTriggers, setReq, &setReply); err != nil {
t.Error("Got error on ApierV1.ResetActionTiming: ", err.Error())
} else if setReply != utils.OK {
diff --git a/apier/v1/triggers.go b/apier/v1/triggers.go
index c5898a54b..e3424a36e 100644
--- a/apier/v1/triggers.go
+++ b/apier/v1/triggers.go
@@ -19,6 +19,7 @@ along with this program. If not, see
package v1
import (
+ "errors"
"strings"
"time"
@@ -185,34 +186,154 @@ func (api *ApierV1) ResetAccountActionTriggers(attr AttrResetAccountActionTrigge
}
type AttrSetAccountActionTriggers struct {
- Tenant string
- Account string
- GroupID string
- UniqueID string
- ThresholdType *string
- ThresholdValue *float64
- Recurrent *bool
- Executed *bool
- MinSleep *string
- ExpirationDate *string
- ActivationDate *string
- BalanceID *string
- BalanceType *string
- BalanceDestinationIds *[]string
- BalanceWeight *float64
- BalanceExpirationDate *string
- BalanceTimingTags *[]string
- BalanceRatingSubject *string
- BalanceCategories *[]string
- BalanceSharedGroups *[]string
- BalanceBlocker *bool
- BalanceDisabled *bool
- MinQueuedItems *int
- ActionsID *string
+ Tenant string
+ Account string
+ GroupID string
+ UniqueID string
+ ActionTrigger map[string]interface{}
}
-func (api *ApierV1) SetAccountActionTriggers(attr AttrSetAccountActionTriggers, reply *string) error {
+// UpdateActionTrigger updates the ActionTrigger if is matching
+func (attr *AttrSetAccountActionTriggers) UpdateActionTrigger(at *engine.ActionTrigger, timezone string) (updated bool, err error) {
+ if at == nil {
+ return false, errors.New("Empty ActionTrigger")
+ }
+ if at.ID == utils.EmptyString { // New AT, update it's data
+ if attr.GroupID == utils.EmptyString {
+ return false, utils.NewErrMandatoryIeMissing(utils.GroupID)
+ }
+ if missing := utils.MissingMapFields(attr.ActionTrigger, []string{"ThresholdType", "ThresholdValue"}); len(missing) != 0 {
+ return false, utils.NewErrMandatoryIeMissing(missing...)
+ }
+ at.ID = attr.GroupID
+ if attr.UniqueID != utils.EmptyString {
+ at.UniqueID = attr.UniqueID
+ }
+ }
+ if attr.GroupID != utils.EmptyString && attr.GroupID != at.ID {
+ return
+ }
+ if attr.UniqueID != utils.EmptyString && attr.UniqueID != at.UniqueID {
+ return
+ }
+ // at matches
+ updated = true
+ if thr, has := attr.ActionTrigger[utils.ThresholdType]; has {
+ at.ThresholdType = utils.IfaceAsString(thr)
+ }
+ if thr, has := attr.ActionTrigger[utils.ThresholdValue]; has {
+ if at.ThresholdValue, err = utils.IfaceAsFloat64(thr); err != nil {
+ return
+ }
+ }
+ if rec, has := attr.ActionTrigger[utils.Recurrent]; has {
+ if at.Recurrent, err = utils.IfaceAsBool(rec); err != nil {
+ return
+ }
+ }
+ if exec, has := attr.ActionTrigger[utils.Executed]; has {
+ if at.Executed, err = utils.IfaceAsBool(exec); err != nil {
+ return
+ }
+ }
+ if minS, has := attr.ActionTrigger[utils.MinSleep]; has {
+ if at.MinSleep, err = utils.IfaceAsDuration(minS); err != nil {
+ return
+ }
+ }
+ if exp, has := attr.ActionTrigger[utils.ExpirationDate]; has {
+ if at.ExpirationDate, err = utils.IfaceAsTime(exp, timezone); err != nil {
+ return
+ }
+ }
+ if act, has := attr.ActionTrigger[utils.ActivationDate]; has {
+ if at.ActivationDate, err = utils.IfaceAsTime(act, timezone); err != nil {
+ return
+ }
+ }
+ if at.Balance == nil {
+ at.Balance = &engine.BalanceFilter{}
+ }
+ if bid, has := attr.ActionTrigger[utils.BalanceID]; has {
+ at.Balance.ID = utils.StringPointer(utils.IfaceAsString(bid))
+ }
+ if btype, has := attr.ActionTrigger[utils.BalanceType]; has {
+ at.Balance.Type = utils.StringPointer(utils.IfaceAsString(btype))
+ }
+ if bdest, has := attr.ActionTrigger[utils.BalanceDestinationIds]; has {
+ var bdIds []string
+ if bdIds, err = utils.IfaceAsSliceString(bdest); err != nil {
+ return
+ }
+ at.Balance.DestinationIDs = utils.StringMapPointer(utils.NewStringMap(bdIds...))
+ }
+ if bweight, has := attr.ActionTrigger[utils.BalanceWeight]; has {
+ var bw float64
+ if bw, err = utils.IfaceAsFloat64(bweight); err != nil {
+ return
+ }
+ at.Balance.Weight = utils.Float64Pointer(bw)
+ }
+ if exp, has := attr.ActionTrigger[utils.BalanceExpirationDate]; has {
+ var balanceExpTime time.Time
+ if balanceExpTime, err = utils.IfaceAsTime(exp, timezone); err != nil {
+ return
+ }
+ at.Balance.ExpirationDate = utils.TimePointer(balanceExpTime)
+ }
+ if bTimeTag, has := attr.ActionTrigger[utils.BalanceTimingTags]; has {
+ var timeTag []string
+ if timeTag, err = utils.IfaceAsSliceString(bTimeTag); err != nil {
+ return
+ }
+ at.Balance.TimingIDs = utils.StringMapPointer(utils.NewStringMap(timeTag...))
+ }
+ if brs, has := attr.ActionTrigger[utils.BalanceRatingSubject]; has {
+ at.Balance.RatingSubject = utils.StringPointer(utils.IfaceAsString(brs))
+ }
+ if bcat, has := attr.ActionTrigger[utils.BalanceCategories]; has {
+ var cat []string
+ if cat, err = utils.IfaceAsSliceString(bcat); err != nil {
+ return
+ }
+ at.Balance.Categories = utils.StringMapPointer(utils.NewStringMap(cat...))
+ }
+ if bsg, has := attr.ActionTrigger[utils.BalanceSharedGroups]; has {
+ var shrgrps []string
+ if shrgrps, err = utils.IfaceAsSliceString(bsg); err != nil {
+ return
+ }
+ at.Balance.SharedGroups = utils.StringMapPointer(utils.NewStringMap(shrgrps...))
+ }
+ if bb, has := attr.ActionTrigger[utils.BalanceBlocker]; has {
+ var bBlocker bool
+ if bBlocker, err = utils.IfaceAsBool(bb); err != nil {
+ return
+ }
+ at.Balance.Blocker = utils.BoolPointer(bBlocker)
+ }
+ if bd, has := attr.ActionTrigger[utils.BalanceDisabled]; has {
+ var bDis bool
+ if bDis, err = utils.IfaceAsBool(bd); err != nil {
+ return
+ }
+ at.Balance.Disabled = utils.BoolPointer(bDis)
+ }
+ if minQ, has := attr.ActionTrigger[utils.MinQueuedItems]; has {
+ var mQ int64
+ if mQ, err = utils.IfaceAsInt64(minQ); err != nil {
+ return
+ }
+ at.MinQueuedItems = int(mQ)
+ }
+ if accID, has := attr.ActionTrigger[utils.ActionsID]; has {
+ at.ActionsID = utils.IfaceAsString(accID)
+ }
+ return
+}
+// SetAccountActionTriggers updates or creates if not present the ActionTrigger for an Account
+func (api *ApierV1) SetAccountActionTriggers(attr AttrSetAccountActionTriggers, reply *string) error {
if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Account"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
@@ -224,92 +345,23 @@ func (api *ApierV1) SetAccountActionTriggers(attr AttrSetAccountActionTriggers,
} else {
return 0, err
}
+ var foundOne bool
for _, at := range account.ActionTriggers {
- if (attr.UniqueID == "" || at.UniqueID == attr.UniqueID) &&
- (attr.GroupID == "" || at.ID == attr.GroupID) {
- // we have a winner
- if attr.ThresholdType != nil {
- at.ThresholdType = *attr.ThresholdType
- }
- if attr.ThresholdValue != nil {
- at.ThresholdValue = *attr.ThresholdValue
- }
- if attr.Recurrent != nil {
- at.Recurrent = *attr.Recurrent
- }
- if attr.Executed != nil {
- at.Executed = *attr.Executed
- }
- if attr.MinSleep != nil {
- minSleep, err := utils.ParseDurationWithNanosecs(*attr.MinSleep)
- if err != nil {
- return 0, err
- }
- at.MinSleep = minSleep
- }
- if attr.ExpirationDate != nil {
- expTime, err := utils.ParseTimeDetectLayout(*attr.ExpirationDate,
- api.Config.GeneralCfg().DefaultTimezone)
- if err != nil {
- return 0, err
- }
- at.ExpirationDate = expTime
- }
- if attr.ActivationDate != nil {
- actTime, err := utils.ParseTimeDetectLayout(*attr.ActivationDate,
- api.Config.GeneralCfg().DefaultTimezone)
- if err != nil {
- return 0, err
- }
- at.ActivationDate = actTime
- }
- at.Balance = &engine.BalanceFilter{}
- if attr.BalanceID != nil {
- at.Balance.ID = attr.BalanceID
- }
- if attr.BalanceType != nil {
- at.Balance.Type = attr.BalanceType
- }
- if attr.BalanceDestinationIds != nil {
- at.Balance.DestinationIDs = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceDestinationIds...))
- }
- if attr.BalanceWeight != nil {
- at.Balance.Weight = attr.BalanceWeight
- }
- if attr.BalanceExpirationDate != nil {
- balanceExpTime, err := utils.ParseTimeDetectLayout(*attr.BalanceExpirationDate,
- api.Config.GeneralCfg().DefaultTimezone)
- if err != nil {
- return 0, err
- }
- at.Balance.ExpirationDate = &balanceExpTime
- }
- if attr.BalanceTimingTags != nil {
- at.Balance.TimingIDs = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceTimingTags...))
- }
- if attr.BalanceRatingSubject != nil {
- at.Balance.RatingSubject = attr.BalanceRatingSubject
- }
- if attr.BalanceCategories != nil {
- at.Balance.Categories = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceCategories...))
- }
- if attr.BalanceSharedGroups != nil {
- at.Balance.SharedGroups = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceSharedGroups...))
- }
- if attr.BalanceBlocker != nil {
- at.Balance.Blocker = attr.BalanceBlocker
- }
- if attr.BalanceDisabled != nil {
- at.Balance.Disabled = attr.BalanceDisabled
- }
- if attr.MinQueuedItems != nil {
- at.MinQueuedItems = *attr.MinQueuedItems
- }
- if attr.ActionsID != nil {
- at.ActionsID = *attr.ActionsID
- }
+ if updated, err := attr.UpdateActionTrigger(at,
+ api.Config.GeneralCfg().DefaultTimezone); err != nil {
+ return 0, err
+ } else if updated && !foundOne {
+ foundOne = true
+ }
+ }
+ if !foundOne { // Did not find one to update, create a new AT
+ at := new(engine.ActionTrigger)
+ if updated, err := attr.UpdateActionTrigger(at,
+ api.Config.GeneralCfg().DefaultTimezone); err != nil {
+ return 0, err
+ } else if updated { // Adding a new AT
+ account.ActionTriggers = append(account.ActionTriggers, at)
}
-
}
account.ExecuteActionTriggers(nil)
return 0, api.DataManager.SetAccount(account)
diff --git a/apier/v2/apierv2_it_test.go b/apier/v2/apierv2_it_test.go
index 14d339c35..c1698abfb 100644
--- a/apier/v2/apierv2_it_test.go
+++ b/apier/v2/apierv2_it_test.go
@@ -145,14 +145,16 @@ func TestApierV2itSetAction(t *testing.T) {
}
func TestApierV2itSetAccountActionTriggers(t *testing.T) {
- attrs := AttrSetAccountActionTriggers{
- Tenant: "cgrates.org",
- Account: "dan",
- GroupID: utils.StringPointer("MONITOR_MAX_BALANCE"),
- ThresholdType: utils.StringPointer(utils.TRIGGER_MAX_BALANCE),
- ThresholdValue: utils.Float64Pointer(50),
- BalanceType: utils.StringPointer(utils.MONETARY),
- ActionsID: utils.StringPointer("DISABLE_ACCOUNT"),
+ attrs := v1.AttrSetAccountActionTriggers{
+ Tenant: "cgrates.org",
+ Account: "dan",
+ GroupID: "MONITOR_MAX_BALANCE",
+ ActionTrigger: map[string]interface{}{
+ utils.ThresholdType: utils.TRIGGER_MAX_BALANCE,
+ utils.ThresholdValue: 50,
+ utils.BalanceType: utils.MONETARY,
+ utils.ActionsID: "DISABLE_ACCOUNT",
+ },
}
var reply string
if err := apierRPC.Call(utils.ApierV2SetAccountActionTriggers, attrs, &reply); err != nil {
@@ -161,16 +163,16 @@ func TestApierV2itSetAccountActionTriggers(t *testing.T) {
var ats engine.ActionTriggers
if err := apierRPC.Call(utils.ApierV2GetAccountActionTriggers, utils.TenantAccount{Tenant: "cgrates.org", Account: "dan"}, &ats); err != nil {
t.Error(err)
- } else if len(ats) != 1 || ats[0].ID != *attrs.GroupID || ats[0].ThresholdValue != 50.0 {
+ } else if len(ats) != 1 || ats[0].ID != attrs.GroupID || ats[0].ThresholdValue != 50.0 {
t.Errorf("Received: %+v", ats)
}
- attrs.ThresholdValue = utils.Float64Pointer(55) // Change the threshold
+ attrs.ActionTrigger[utils.ThresholdValue] = 55 // Change the threshold
if err := apierRPC.Call(utils.ApierV2SetAccountActionTriggers, attrs, &reply); err != nil {
t.Error(err)
}
if err := apierRPC.Call(utils.ApierV2GetAccountActionTriggers, utils.TenantAccount{Tenant: "cgrates.org", Account: "dan"}, &ats); err != nil {
t.Error(err)
- } else if len(ats) != 1 || ats[0].ID != *attrs.GroupID || ats[0].ThresholdValue != 55.0 {
+ } else if len(ats) != 1 || ats[0].ID != attrs.GroupID || ats[0].ThresholdValue != 55.0 {
t.Errorf("Received: %+v", ats)
}
}
diff --git a/apier/v2/triggers.go b/apier/v2/triggers.go
deleted file mode 100644
index 2c28a5dfb..000000000
--- a/apier/v2/triggers.go
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
-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 v2
-
-import (
- "errors"
-
- "github.com/cgrates/cgrates/config"
- "github.com/cgrates/cgrates/engine"
- "github.com/cgrates/cgrates/guardian"
- "github.com/cgrates/cgrates/utils"
-)
-
-type AttrSetAccountActionTriggers struct {
- Tenant string
- Account string
- GroupID *string
- UniqueID *string
- ThresholdType *string
- ThresholdValue *float64
- Recurrent *bool
- Executed *bool
- MinSleep *string
- ExpirationDate *string
- ActivationDate *string
- BalanceID *string
- BalanceType *string
- BalanceDestinationIds *[]string
- BalanceWeight *float64
- BalanceExpirationDate *string
- BalanceTimingTags *[]string
- BalanceRatingSubject *string
- BalanceCategories *[]string
- BalanceSharedGroups *[]string
- BalanceBlocker *bool
- BalanceDisabled *bool
- MinQueuedItems *int
- ActionsID *string
-}
-
-func (attr *AttrSetAccountActionTriggers) UpdateActionTrigger(at *engine.ActionTrigger, timezone string) (updated bool, err error) {
- if at == nil {
- return false, errors.New("Empty ActionTrigger")
- }
- if at.ID == "" { // New AT, update it's data
- if missing := utils.MissingStructFields(attr, []string{"GroupID", "ThresholdType", "ThresholdValue"}); len(missing) != 0 {
- return false, utils.NewErrMandatoryIeMissing(missing...)
- }
- at.ID = *attr.GroupID
- if attr.UniqueID != nil {
- at.UniqueID = *attr.UniqueID
- }
- }
- if attr.GroupID != nil && *attr.GroupID != at.ID {
- return
- }
- if attr.UniqueID != nil && *attr.UniqueID != at.UniqueID {
- return
- }
- // at matches
- updated = true
- if attr.ThresholdType != nil {
- at.ThresholdType = *attr.ThresholdType
- }
- if attr.ThresholdValue != nil {
- at.ThresholdValue = *attr.ThresholdValue
- }
- if attr.Recurrent != nil {
- at.Recurrent = *attr.Recurrent
- }
- if attr.Executed != nil {
- at.Executed = *attr.Executed
- }
- if attr.MinSleep != nil {
- if at.MinSleep, err = utils.ParseDurationWithNanosecs(*attr.MinSleep); err != nil {
- return
- }
- }
- if attr.ExpirationDate != nil {
- if at.ExpirationDate, err = utils.ParseTimeDetectLayout(*attr.ExpirationDate, timezone); err != nil {
- return
- }
- }
- if attr.ActivationDate != nil {
- if at.ActivationDate, err = utils.ParseTimeDetectLayout(*attr.ActivationDate, timezone); err != nil {
- return
- }
- }
- if at.Balance == nil {
- at.Balance = &engine.BalanceFilter{}
- }
- if attr.BalanceID != nil {
- at.Balance.ID = attr.BalanceID
- }
- if attr.BalanceType != nil {
- at.Balance.Type = attr.BalanceType
- }
- if attr.BalanceDestinationIds != nil {
- at.Balance.DestinationIDs = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceDestinationIds...))
- }
- if attr.BalanceWeight != nil {
- at.Balance.Weight = attr.BalanceWeight
- }
- if attr.BalanceExpirationDate != nil {
- balanceExpTime, err := utils.ParseTimeDetectLayout(*attr.BalanceExpirationDate, timezone)
- if err != nil {
- return false, err
- }
- at.Balance.ExpirationDate = &balanceExpTime
- }
- if attr.BalanceTimingTags != nil {
- at.Balance.TimingIDs = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceTimingTags...))
- }
- if attr.BalanceRatingSubject != nil {
- at.Balance.RatingSubject = attr.BalanceRatingSubject
- }
- if attr.BalanceCategories != nil {
- at.Balance.Categories = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceCategories...))
- }
- if attr.BalanceSharedGroups != nil {
- at.Balance.SharedGroups = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceSharedGroups...))
- }
- if attr.BalanceBlocker != nil {
- at.Balance.Blocker = attr.BalanceBlocker
- }
- if attr.BalanceDisabled != nil {
- at.Balance.Disabled = attr.BalanceDisabled
- }
- if attr.MinQueuedItems != nil {
- at.MinQueuedItems = *attr.MinQueuedItems
- }
- if attr.ActionsID != nil {
- at.ActionsID = *attr.ActionsID
- }
- return
-}
-
-// SetAccountActionTriggers Updates or Creates ActionTriggers for an Account
-func (self *ApierV2) SetAccountActionTriggers(attr AttrSetAccountActionTriggers, reply *string) error {
- if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Account"}); len(missing) != 0 {
- return utils.NewErrMandatoryIeMissing(missing...)
- }
- accID := utils.ConcatenatedKey(attr.Tenant, attr.Account)
- var account *engine.Account
- _, err := guardian.Guardian.Guard(func() (interface{}, error) {
- if acc, err := self.DataManager.GetAccount(accID); err == nil {
- account = acc
- } else {
- return 0, err
- }
- var foundOne bool
- for _, at := range account.ActionTriggers {
- if updated, err := attr.UpdateActionTrigger(at,
- self.Config.GeneralCfg().DefaultTimezone); err != nil {
- return 0, err
- } else if updated && !foundOne {
- foundOne = true
- }
- }
- if !foundOne { // Did not find one to update, create a new AT
- at := new(engine.ActionTrigger)
- if updated, err := attr.UpdateActionTrigger(at,
- self.Config.GeneralCfg().DefaultTimezone); err != nil {
- return 0, err
- } else if updated { // Adding a new AT
- account.ActionTriggers = append(account.ActionTriggers, at)
- }
- }
- account.ExecuteActionTriggers(nil)
- return 0, self.DataManager.SetAccount(account)
- }, config.CgrConfig().GeneralCfg().LockingTimeout, utils.ACCOUNT_PREFIX+accID)
- if err != nil {
- *reply = err.Error()
- return err
- }
- *reply = utils.OK
- return nil
-}
diff --git a/utils/consts.go b/utils/consts.go
index 5f47768ab..b828a9dbb 100755
--- a/utils/consts.go
+++ b/utils/consts.go
@@ -412,6 +412,15 @@ const (
StatID = "StatID"
BalanceType = "BalanceType"
BalanceID = "BalanceID"
+ BalanceDestinationIds = "BalanceDestinationIds"
+ BalanceWeight = "BalanceWeight"
+ BalanceExpirationDate = "BalanceExpirationDate"
+ BalanceTimingTags = "BalanceTimingTags"
+ BalanceRatingSubject = "BalanceRatingSubject"
+ BalanceCategories = "BalanceCategories"
+ BalanceSharedGroups = "BalanceSharedGroups"
+ BalanceBlocker = "BalanceBlocker"
+ BalanceDisabled = "BalanceDisabled"
Units = "Units"
AccountUpdate = "AccountUpdate"
BalanceUpdate = "BalanceUpdate"
@@ -572,6 +581,15 @@ const (
ExportPath = "ExportPath"
ExportID = "ExportID"
ExportFileName = "ExportFileName"
+ GroupID = "GroupID"
+ ThresholdType = "ThresholdType"
+ ThresholdValue = "ThresholdValue"
+ Recurrent = "Recurrent"
+ Executed = "Executed"
+ MinSleep = "MinSleep"
+ ActivationDate = "ActivationDate"
+ ExpirationDate = "ExpirationDate"
+ MinQueuedItems = "MinQueuedItems"
)
// Migrator Action
diff --git a/utils/reflect.go b/utils/reflect.go
index 79338b1b5..87a06b98f 100644
--- a/utils/reflect.go
+++ b/utils/reflect.go
@@ -272,6 +272,84 @@ func IfaceAsString(fld interface{}) (out string) {
}
}
+// IfaceAsSliceString is trying to convert the interface to a slice of strings
+func IfaceAsSliceString(fld interface{}) (out []string, err error) {
+ switch value := fld.(type) {
+ case nil:
+ return
+ case []int:
+ out = make([]string, len(value))
+ for i, val := range value {
+ out[i] = strconv.Itoa(val)
+ }
+ case []int32:
+ out = make([]string, len(value))
+ for i, val := range value {
+ out[i] = strconv.FormatInt(int64(val), 10)
+ }
+ case []int64:
+ out = make([]string, len(value))
+ for i, val := range value {
+ out[i] = strconv.FormatInt(val, 10)
+ }
+ case []uint32:
+ out = make([]string, len(value))
+ for i, val := range value {
+ out[i] = strconv.FormatUint(uint64(val), 10)
+ }
+ case []uint64:
+ out = make([]string, len(value))
+ for i, val := range value {
+ out[i] = strconv.FormatUint(val, 10)
+ }
+ case []bool:
+ out = make([]string, len(value))
+ for i, val := range value {
+ out[i] = strconv.FormatBool(val)
+ }
+ case []float32:
+ out = make([]string, len(value))
+ for i, val := range value {
+ out[i] = strconv.FormatFloat(float64(val), 'f', -1, 64)
+ }
+ case []float64:
+ out = make([]string, len(value))
+ for i, val := range value {
+ out[i] = strconv.FormatFloat(val, 'f', -1, 64)
+ }
+ case [][]uint8:
+ out = make([]string, len(value))
+ for i, val := range value {
+ out[i] = string(val) // byte is an alias for uint8 conversions implicit
+ }
+ case []time.Duration:
+ out = make([]string, len(value))
+ for i, val := range value {
+ out[i] = val.String()
+ }
+ case []time.Time:
+ out = make([]string, len(value))
+ for i, val := range value {
+ out[i] = val.Format(time.RFC3339)
+ }
+ case []net.IP:
+ out = make([]string, len(value))
+ for i, val := range value {
+ out[i] = val.String()
+ }
+ case []string:
+ out = value
+ case []interface{}:
+ out = make([]string, len(value))
+ for i, val := range value {
+ out[i] = IfaceAsString(val)
+ }
+ default: // Maybe we are lucky and the value converts to string
+ err = fmt.Errorf("cannot convert field: %+v to bool", value)
+ }
+ return
+}
+
// AsMapStringIface converts an item (mostly struct) as map[string]interface{}
func AsMapStringIface(item interface{}) (map[string]interface{}, error) {
out := make(map[string]interface{})
diff --git a/utils/struct.go b/utils/struct.go
index dccd01fed..81d17f946 100644
--- a/utils/struct.go
+++ b/utils/struct.go
@@ -68,6 +68,28 @@ func NonemptyStructFields(s interface{}) map[string]interface{} {
return fields
}
+// MissingMapFields detects missing field values based on mandatory field names from a map[string]interface{}
+func MissingMapFields(s map[string]interface{}, mandatories []string) []string {
+ missing := []string{}
+ for _, fieldName := range mandatories {
+ if fldval, has := s[fieldName]; !has {
+ missing = append(missing, fieldName)
+ } else {
+ fld := reflect.ValueOf(fldval)
+ // sanitize the string fields before checking
+ if fld.Kind() == reflect.String && fld.CanSet() {
+ fld.SetString(strings.TrimSpace(fld.String()))
+ }
+ if (fld.Kind() == reflect.String && fld.String() == "") ||
+ ((fld.Kind() == reflect.Slice || fld.Kind() == reflect.Map) && fld.Len() == 0) ||
+ (fld.Kind() == reflect.Int && fld.Int() == 0) {
+ missing = append(missing, fieldName)
+ }
+ }
+ }
+ return missing
+}
+
// Converts a struct to map
/*func StrucToMap(s interface{}) map[string]interface{} {
mp := make(map[string]interface{})