From 511f5002d4d89fc12301788200d64f95124e511c Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 21 Jan 2016 11:27:09 +0200 Subject: [PATCH] cond lang refactor for better design --- apier/v2/accounts.go | 98 +++++++++++++++++++++++ utils/cond_loader.go | 184 +++++++++++++++++++++++++------------------ 2 files changed, 205 insertions(+), 77 deletions(-) diff --git a/apier/v2/accounts.go b/apier/v2/accounts.go index 73a8f9fe7..d1c49b5fd 100644 --- a/apier/v2/accounts.go +++ b/apier/v2/accounts.go @@ -77,3 +77,101 @@ func (self *ApierV2) GetAccount(attr *utils.AttrGetAccount, reply *engine.Accoun *reply = *account return nil } + +type AttrSetAccount struct { + Tenant string + Account string + ActionPlanId string + ActionTriggersId string + AllowNegative *bool + Disabled *bool + ReloadScheduler bool +} + +func (self *ApierV2) SetAccount(attr AttrSetAccount, reply *string) error { + if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Account"}); len(missing) != 0 { + return utils.NewErrMandatoryIeMissing(missing...) + } + var schedulerReloadNeeded = false + accID := utils.AccountKey(attr.Tenant, attr.Account) + var ub *engine.Account + _, err := engine.Guardian.Guard(func() (interface{}, error) { + if bal, _ := self.AccountDb.GetAccount(accID); bal != nil { + ub = bal + } else { // Not found in db, create it here + ub = &engine.Account{ + Id: accID, + } + } + if len(attr.ActionPlanId) != 0 { + _, err := engine.Guardian.Guard(func() (interface{}, error) { + var ap *engine.ActionPlan + var err error + ap, err = self.RatingDb.GetActionPlan(attr.ActionPlanId, false) + if err != nil { + return 0, err + } + if _, exists := ap.AccountIDs[accID]; !exists { + if ap.AccountIDs == nil { + ap.AccountIDs = make(map[string]struct{}) + } + ap.AccountIDs[accID] = struct{}{} + schedulerReloadNeeded = true + // create tasks + for _, at := range ap.ActionTimings { + if at.IsASAP() { + t := &engine.Task{ + Uuid: utils.GenUUID(), + AccountID: accID, + ActionsID: at.ActionsID, + } + if err = self.RatingDb.PushTask(t); err != nil { + return 0, err + } + } + } + if err := self.RatingDb.SetActionPlan(attr.ActionPlanId, ap); err != nil { + return 0, err + } + // update cache + self.RatingDb.CacheRatingPrefixValues(map[string][]string{utils.ACTION_PLAN_PREFIX: []string{utils.ACTION_PLAN_PREFIX + attr.ActionPlanId}}) + } + return 0, nil + }, 0, utils.ACTION_PLAN_PREFIX) + if err != nil { + return 0, err + } + } + + if len(attr.ActionTriggersId) != 0 { + atrs, err := self.RatingDb.GetActionTriggers(attr.ActionTriggersId) + if err != nil { + return 0, err + } + ub.ActionTriggers = atrs + ub.InitCounters() + } + if attr.AllowNegative != nil { + ub.AllowNegative = *attr.AllowNegative + } + if attr.Disabled != nil { + ub.Disabled = *attr.Disabled + } + // All prepared, save account + if err := self.AccountDb.SetAccount(ub); err != nil { + return 0, err + } + return 0, nil + }, 0, accID) + if err != nil { + return utils.NewErrServerError(err) + } + if attr.ReloadScheduler && schedulerReloadNeeded { + // reload scheduler + if self.Sched != nil { + self.Sched.Reload(true) + } + } + *reply = utils.OK // This will mark saving of the account, error still can show up in actionTimingsId + return nil +} diff --git a/utils/cond_loader.go b/utils/cond_loader.go index da7123d73..f14e49607 100644 --- a/utils/cond_loader.go +++ b/utils/cond_loader.go @@ -45,21 +45,108 @@ const ( CondHAS = "*has" ) +var operatorMap = map[string]func(field, value interface{}) (bool, error){ + CondEQ: func(field, value interface{}) (bool, error) { + return value == field, nil + }, + CondGT: func(field, value interface{}) (bool, error) { + var of, vf float64 + var ok bool + if of, ok = field.(float64); !ok { + return false, NewErrInvalidArgument(field) + } + if vf, ok = value.(float64); !ok { + return false, NewErrInvalidArgument(value) + } + return of > vf, nil + }, + CondGTE: func(field, value interface{}) (bool, error) { + var of, vf float64 + var ok bool + if of, ok = field.(float64); !ok { + return false, NewErrInvalidArgument(field) + } + if vf, ok = value.(float64); !ok { + return false, NewErrInvalidArgument(value) + } + return of >= vf, nil + }, + CondLT: func(field, value interface{}) (bool, error) { + var of, vf float64 + var ok bool + if of, ok = field.(float64); !ok { + return false, NewErrInvalidArgument(field) + } + if vf, ok = value.(float64); !ok { + return false, NewErrInvalidArgument(value) + } + return of < vf, nil + }, + CondLTE: func(field, value interface{}) (bool, error) { + var of, vf float64 + var ok bool + if of, ok = field.(float64); !ok { + return false, NewErrInvalidArgument(field) + } + if vf, ok = value.(float64); !ok { + return false, NewErrInvalidArgument(value) + } + return of <= vf, nil + }, + CondEXP: func(field, value interface{}) (bool, error) { + var expDate time.Time + var ok bool + if expDate, ok = field.(time.Time); !ok { + return false, NewErrInvalidArgument(field) + } + var expired bool + if expired, ok = value.(bool); !ok { + return false, NewErrInvalidArgument(value) + } + if expired { // check for expiration + return !expDate.IsZero() && expDate.Before(time.Now()), nil + } else { // check not expired + return expDate.IsZero() || expDate.After(time.Now()), nil + } + }, + CondHAS: func(field, value interface{}) (bool, error) { + var strMap StringMap + var ok bool + if strMap, ok = field.(StringMap); !ok { + return false, NewErrInvalidArgument(field) + } + var strSlice []interface{} + if strSlice, ok = value.([]interface{}); !ok { + return false, NewErrInvalidArgument(value) + } + for _, str := range strSlice { + if !strMap[str.(string)] { + return false, nil + } + } + return true, nil + }, +} + func NewErrInvalidArgument(arg interface{}) error { return fmt.Errorf("INVALID_ARGUMENT: %v", arg) } -type condElement interface { - addChild(condElement) error +type compositeElement interface { + element + addChild(element) error +} + +type element interface { checkStruct(interface{}) (bool, error) } type operatorSlice struct { operator string - slice []condElement + slice []element } -func (os *operatorSlice) addChild(ce condElement) error { +func (os *operatorSlice) addChild(ce element) error { os.slice = append(os.slice, ce) return nil } @@ -91,10 +178,10 @@ func (os *operatorSlice) checkStruct(o interface{}) (bool, error) { type keyStruct struct { key string - elem condElement + elem element } -func (ks *keyStruct) addChild(ce condElement) error { +func (ks *keyStruct) addChild(ce element) error { ks.elem = ce return nil } @@ -115,69 +202,11 @@ type operatorValue struct { value interface{} } -func (ov *operatorValue) addChild(condElement) error { return ErrNotImplemented } func (ov *operatorValue) checkStruct(o interface{}) (bool, error) { - // no conversion - if ov.operator == CondEQ { - return ov.value == o, nil + if f, ok := operatorMap[ov.operator]; ok { + return f(o, ov.value) } - // StringMap conversion - if ov.operator == CondHAS { - var strMap StringMap - var ok bool - if strMap, ok = o.(StringMap); !ok { - return false, NewErrInvalidArgument(o) - } - var strSlice []interface{} - if strSlice, ok = ov.value.([]interface{}); !ok { - return false, NewErrInvalidArgument(ov.value) - } - for _, str := range strSlice { - if !strMap[str.(string)] { - return false, nil - } - } - return true, nil - } - // date conversion - if ov.operator == CondEXP { - var expDate time.Time - var ok bool - if expDate, ok = o.(time.Time); !ok { - return false, NewErrInvalidArgument(o) - } - var expired bool - if expired, ok = ov.value.(bool); !ok { - return false, NewErrInvalidArgument(ov.value) - } - if expired { // check for expiration - return !expDate.IsZero() && expDate.Before(time.Now()), nil - } else { // check not expired - return expDate.IsZero() || expDate.After(time.Now()), nil - } - } - - // float conversion - var of, vf float64 - var ok bool - if of, ok = o.(float64); !ok { - return false, NewErrInvalidArgument(o) - } - if vf, ok = ov.value.(float64); !ok { - return false, NewErrInvalidArgument(ov.value) - } - switch ov.operator { - case CondGT: - return of > vf, nil - case CondGTE: - return of >= vf, nil - case CondLT: - return of < vf, nil - case CondLTE: - return of <= vf, nil - - } - return true, nil + return false, nil } type keyValue struct { @@ -185,19 +214,20 @@ type keyValue struct { value interface{} } -func (kv *keyValue) addChild(condElement) error { return ErrNotImplemented } func (kv *keyValue) checkStruct(o interface{}) (bool, error) { obj := reflect.ValueOf(o) if obj.Kind() == reflect.Ptr { obj = obj.Elem() } value := obj.FieldByName(kv.key) + if !value.IsValid() { + return false, NewErrInvalidArgument(kv.key) + } return value.Interface() == kv.value, nil } type trueElement struct{} -func (te *trueElement) addChild(condElement) error { return ErrNotImplemented } func (te *trueElement) checkStruct(o interface{}) (bool, error) { return true, nil } @@ -211,12 +241,12 @@ func notEmpty(x interface{}) bool { } type CondLoader struct { - rootElement condElement + rootElement element } -func (cp *CondLoader) load(a map[string]interface{}, parentElement condElement) (condElement, error) { +func (cp *CondLoader) load(a map[string]interface{}, parentElement compositeElement) (element, error) { for key, value := range a { - var currentElement condElement + var currentElement element switch t := value.(type) { case []interface{}: if key == CondHAS { @@ -224,13 +254,13 @@ func (cp *CondLoader) load(a map[string]interface{}, parentElement condElement) } else { currentElement = &operatorSlice{operator: key} for _, e := range t { - cp.load(e.(map[string]interface{}), currentElement) + cp.load(e.(map[string]interface{}), currentElement.(compositeElement)) } } case map[string]interface{}: currentElement = &keyStruct{key: key} //log.Print("map: ", t) - cp.load(t, currentElement) + cp.load(t, currentElement.(compositeElement)) case interface{}: if isOperator(key) { currentElement = &operatorValue{operator: key, value: t} @@ -241,13 +271,13 @@ func (cp *CondLoader) load(a map[string]interface{}, parentElement condElement) default: return nil, ErrParserError } - if parentElement != nil { + if parentElement != nil { // normal recurrent action parentElement.addChild(currentElement) } else { - if len(a) > 1 { + if len(a) > 1 { // we have more keys in the map parentElement = &operatorSlice{operator: CondAND} parentElement.addChild(currentElement) - } else { + } else { // it was only one key value return currentElement, nil } }