diff --git a/apier/v1/tpaccountactions.go b/apier/v1/tpaccountactions.go index a6d138dfc..52a37c40a 100644 --- a/apier/v1/tpaccountactions.go +++ b/apier/v1/tpaccountactions.go @@ -27,10 +27,10 @@ import ( // Creates a new AccountActions profile within a tariff plan func (self *ApierV1) SetTPAccountActions(attrs utils.TPAccountActions, reply *string) error { if missing := utils.MissingStructFields(&attrs, - []string{"TPid", "AccountActionsId", "Tenant", "Account", "Direction", "ActionTimingsId", "ActionTriggersId"}); len(missing) != 0 { + []string{"TPid", "LoadId", "Tenant", "Account", "Direction", "ActionTimingsId", "ActionTriggersId"}); len(missing) != 0 { return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) } - if err := self.StorDb.SetTPAccountActions(attrs.TPid, map[string]*utils.TPAccountActions{attrs.AccountActionsId: &attrs}); err != nil { + if err := self.StorDb.SetTPAccountActions(attrs.TPid, map[string]*utils.TPAccountActions{attrs.KeyId(): &attrs}); err != nil { return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) } *reply = "OK" @@ -39,26 +39,33 @@ func (self *ApierV1) SetTPAccountActions(attrs utils.TPAccountActions, reply *st type AttrGetTPAccountActions struct { TPid string // Tariff plan id - AccountActionsId string // AccountActions id + LoadId string // AccountActions id + } // Queries specific AccountActions profile on tariff plan -func (self *ApierV1) GetTPAccountActions(attrs AttrGetTPAccountActions, reply *utils.TPAccountActions) error { - if missing := utils.MissingStructFields(&attrs, []string{"TPid", "AccountActionsId"}); len(missing) != 0 { //Params missing +func (self *ApierV1) GetTPAccountActions(attrs utils.TPAccountActions, reply *[]*utils.TPAccountActions) error { + mndtryFlds := []string{"TPid", "LoadId"} + if len(attrs.Account) != 0 { // If account provided as filter, make all related fields mandatory + mndtryFlds = append(mndtryFlds, "Tenant", "Account", "Direction") + } + if missing := utils.MissingStructFields(&attrs, mndtryFlds); len(missing) != 0 { //Params missing return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) } - if aa, err := self.StorDb.GetTpAccountActions(attrs.TPid, attrs.AccountActionsId); err != nil { + if aa, err := self.StorDb.GetTpAccountActions(&attrs); err != nil { return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) } else if len(aa) == 0 { return errors.New(utils.ERR_NOT_FOUND) } else { - *reply = utils.TPAccountActions{TPid: attrs.TPid, - AccountActionsId: attrs.AccountActionsId, - Tenant: aa[attrs.AccountActionsId].Tenant, - Account: aa[attrs.AccountActionsId].Account, - Direction: aa[attrs.AccountActionsId].Direction, - ActionTimingsId: aa[attrs.AccountActionsId].ActionTimingsTag, - ActionTriggersId: aa[attrs.AccountActionsId].ActionTriggersTag} + var acts []*utils.TPAccountActions + if len(attrs.Account) != 0 { + acts = []*utils.TPAccountActions{aa[attrs.KeyId()]} + } else { + for _, actLst := range aa { + acts = append(acts, actLst) + } + } + *reply = acts } return nil } @@ -68,7 +75,7 @@ type AttrGetTPAccountActionIds struct { } // Queries AccountActions identities on specific tariff plan. -func (self *ApierV1) GetTPAccountActionIds(attrs AttrGetTPAccountActionIds, reply *[]string) error { +func (self *ApierV1) GetTPAccountActionLoadIds(attrs AttrGetTPAccountActionIds, reply *[]string) error { if missing := utils.MissingStructFields(&attrs, []string{"TPid"}); len(missing) != 0 { //Params missing return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) } @@ -83,11 +90,11 @@ func (self *ApierV1) GetTPAccountActionIds(attrs AttrGetTPAccountActionIds, repl } // Removes specific AccountActions on Tariff plan -func (self *ApierV1) RemTPAccountActions(attrs AttrGetTPAccountActions, reply *string) error { - if missing := utils.MissingStructFields(&attrs, []string{"TPid", "AccountActionsId"}); len(missing) != 0 { //Params missing +func (self *ApierV1) RemTPAccountActions(attrs utils.TPAccountActions, reply *string) error { + if missing := utils.MissingStructFields(&attrs, []string{"TPid", "LoadId", "Tenant", "Account", "Direction"}); len(missing) != 0 { //Params missing return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) } - if err := self.StorDb.RemTPData(utils.TBL_TP_ACCOUNT_ACTIONS, attrs.TPid, attrs.AccountActionsId); err != nil { + if err := self.StorDb.RemTPData(utils.TBL_TP_ACCOUNT_ACTIONS, attrs.TPid, attrs.LoadId, attrs.Tenant, attrs.Account, attrs.Direction); err != nil { return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) } else { *reply = "OK" diff --git a/apier/v1/tpratingplans.go b/apier/v1/tpratingplans.go index c56069160..fdb3e076c 100644 --- a/apier/v1/tpratingplans.go +++ b/apier/v1/tpratingplans.go @@ -28,10 +28,10 @@ import ( // Creates a new DestinationRateTiming profile within a tariff plan func (self *ApierV1) SetTPRatingPlan(attrs utils.TPRatingPlan, reply *string) error { - if missing := utils.MissingStructFields(&attrs, []string{"TPid", "RatingPlanId", "RatingPlans"}); len(missing) != 0 { + if missing := utils.MissingStructFields(&attrs, []string{"TPid", "RatingPlanId", "RatingPlanBindings"}); len(missing) != 0 { return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) } - if err := self.StorDb.SetTPRatingPlans(attrs.TPid, map[string][]*utils.RatingPlan{attrs.RatingPlanId: attrs.RatingPlans}); err != nil { + if err := self.StorDb.SetTPRatingPlans(attrs.TPid, map[string][]*utils.TPRatingPlanBinding{attrs.RatingPlanId: attrs.RatingPlanBindings}); err != nil { return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) } *reply = "OK" diff --git a/apier/v1/tpratingprofiles.go b/apier/v1/tpratingprofiles.go index aa1e8dc79..a56e8d275 100644 --- a/apier/v1/tpratingprofiles.go +++ b/apier/v1/tpratingprofiles.go @@ -28,10 +28,10 @@ import ( // Creates a new RatingProfile within a tariff plan func (self *ApierV1) SetTPRatingProfile(attrs utils.TPRatingProfile, reply *string) error { - if missing := utils.MissingStructFields(&attrs, []string{"TPid", "RatingProfileId", "Tenant", "TOR", "Direction", "Subject", "RatingPlanActivations"}); len(missing) != 0 { + if missing := utils.MissingStructFields(&attrs, []string{"TPid", "LoadId", "Tenant", "TOR", "Direction", "Subject", "RatingPlanActivations"}); len(missing) != 0 { return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) } - if err := self.StorDb.SetTPRatingProfiles(attrs.TPid, map[string]*utils.TPRatingProfile{attrs.RatingProfileId: &attrs}); err != nil { + if err := self.StorDb.SetTPRatingProfiles(attrs.TPid, map[string]*utils.TPRatingProfile{attrs.KeyId(): &attrs}); err != nil { return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) } *reply = "OK" @@ -40,26 +40,38 @@ func (self *ApierV1) SetTPRatingProfile(attrs utils.TPRatingProfile, reply *stri type AttrGetTPRatingProfile struct { TPid string // Tariff plan id - RatingProfileId string // RatingProfile id + LoadId string // RatingProfile id } // Queries specific RatingProfile on tariff plan -func (self *ApierV1) GetTPRatingProfile(attrs AttrGetTPRatingProfile, reply *utils.TPRatingProfile) error { - if missing := utils.MissingStructFields(&attrs, []string{"TPid", "RatingProfileId"}); len(missing) != 0 { //Params missing +func (self *ApierV1) GetTPRatingProfiles(attrs utils.TPRatingProfile, reply *[]*utils.TPRatingProfile) error { + mndtryFlds := []string{"TPid", "LoadId"} + if len(attrs.Subject) != 0 { // If Subject provided as filter, make all related fields mandatory + mndtryFlds = append(mndtryFlds, "Tenant", "TOR", "Direction","Subject") + } + if missing := utils.MissingStructFields(&attrs, mndtryFlds); len(missing) != 0 { //Params missing return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) } - if dr, err := self.StorDb.GetTPRatingProfile(attrs.TPid, attrs.RatingProfileId); err != nil { + if dr, err := self.StorDb.GetTpRatingProfiles(&attrs); err != nil { return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) } else if dr == nil { return errors.New(utils.ERR_NOT_FOUND) } else { - *reply = *dr + var rpfs []*utils.TPRatingProfile + if len(attrs.Subject) != 0 { + rpfs = []*utils.TPRatingProfile{dr[attrs.KeyId()]} + } else { + for _, rpfLst := range dr { + rpfs = append(rpfs, rpfLst) + } + } + *reply = rpfs } return nil } // Queries RatingProfile identities on specific tariff plan. -func (self *ApierV1) GetTPRatingProfileIds(attrs utils.AttrTPRatingProfileIds, reply *[]string) error { +func (self *ApierV1) GetTPRatingProfileLoadIds(attrs utils.AttrTPRatingProfileIds, reply *[]string) error { if missing := utils.MissingStructFields(&attrs, []string{"TPid"}); len(missing) != 0 { //Params missing return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) } @@ -75,11 +87,11 @@ func (self *ApierV1) GetTPRatingProfileIds(attrs utils.AttrTPRatingProfileIds, r // Removes specific RatingProfile on Tariff plan -func (self *ApierV1) RemTPRatingProfile(attrs AttrGetTPRatingProfile, reply *string) error { - if missing := utils.MissingStructFields(&attrs, []string{"TPid", "RatingProfileId"}); len(missing) != 0 { //Params missing +func (self *ApierV1) RemTPRatingProfile(attrs utils.TPRatingProfile, reply *string) error { + if missing := utils.MissingStructFields(&attrs, []string{"TPid", "LoadId", "Tenant", "TOR", "Direction","Subject"}); len(missing) != 0 { //Params missing return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) } - if err := self.StorDb.RemTPData(utils.TBL_TP_RATE_PROFILES, attrs.TPid, attrs.RatingProfileId); err != nil { + if err := self.StorDb.RemTPData(utils.TBL_TP_RATE_PROFILES, attrs.TPid, attrs.LoadId, attrs.Tenant, attrs.TOR, attrs.Direction, attrs.Subject); err != nil { return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) } else { *reply = "OK" diff --git a/apier/v1/tptimings.go b/apier/v1/tptimings.go index 8562995f6..8231004f9 100644 --- a/apier/v1/tptimings.go +++ b/apier/v1/tptimings.go @@ -94,7 +94,7 @@ func (self *ApierV1) RemTPTiming(attrs AttrGetTPTiming, reply *string) error { if missing := utils.MissingStructFields(&attrs, []string{"TPid", "TimingId"}); len(missing) != 0 { //Params missing return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) } - if err := self.StorDb.RemTPData(utils.TBL_TP_RATES, attrs.TPid, attrs.TimingId); err != nil { + if err := self.StorDb.RemTPData(utils.TBL_TP_TIMINGS, attrs.TPid, attrs.TimingId); err != nil { return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) } else { *reply = "OK" diff --git a/data/storage/mysql/create_tariffplan_tables.sql b/data/storage/mysql/create_tariffplan_tables.sql index 50db48d52..f88123c91 100644 --- a/data/storage/mysql/create_tariffplan_tables.sql +++ b/data/storage/mysql/create_tariffplan_tables.sql @@ -92,7 +92,7 @@ CREATE TABLE `tp_rating_plans` ( CREATE TABLE `tp_rating_profiles` ( `id` int(11) NOT NULL AUTO_INCREMENT, `tpid` varchar(64) NOT NULL, - `tag` varchar(64) NOT NULL, + `loadid` varchar(64) NOT NULL, `tenant` varchar(64) NOT NULL, `tor` varchar(16) NOT NULL, `direction` varchar(8) NOT NULL, @@ -101,8 +101,8 @@ CREATE TABLE `tp_rating_profiles` ( `rating_plan_tag` varchar(64) NOT NULL, `fallback_subjects` varchar(64), PRIMARY KEY (`id`), - KEY `tpid_tag` (`tpid`, `tag`), - UNIQUE KEY `tpid_tag_tenant_tor_dir_subj_atime` (`tpid`,`tag`, `tenant`,`tor`,`direction`,`subject`,`activation_time`) + KEY `tpid_tag` (`tpid`, `loadid`), + UNIQUE KEY `tpid_tag_tenant_tor_dir_subj_atime` (`tpid`,`loadid`, `tenant`,`tor`,`direction`,`subject`,`activation_time`) ); -- @@ -171,7 +171,7 @@ CREATE TABLE `tp_action_triggers` ( CREATE TABLE `tp_account_actions` ( `id` int(11) NOT NULL AUTO_INCREMENT, `tpid` varchar(64) NOT NULL, - `tag` varchar(64) NOT NULL, + `loadid` varchar(64) NOT NULL, `tenant` varchar(64) NOT NULL, `account` varchar(64) NOT NULL, `direction` varchar(8) NOT NULL, @@ -179,5 +179,5 @@ CREATE TABLE `tp_account_actions` ( `action_triggers_tag` varchar(64), PRIMARY KEY (`id`), KEY `tpid` (`tpid`), - UNIQUE KEY `unique_tp_account` (`tpid`,`tag`,`tenant`,`account`,`direction`) + UNIQUE KEY `unique_tp_account` (`tpid`,`loadid`,`tenant`,`account`,`direction`) ); diff --git a/engine/loader_csv.go b/engine/loader_csv.go index 0e0f27835..7b932979e 100644 --- a/engine/loader_csv.go +++ b/engine/loader_csv.go @@ -375,7 +375,7 @@ func (csvr *CSVReader) LoadRatingProfiles() (err error) { } if fallbacksubject != "" { var sslice utils.StringSlice = rpa.FallbackKeys - for _, fbs := range strings.Split(fallbacksubject, ";") { + for _, fbs := range strings.Split(fallbacksubject, FALLBACK_SEP) { newKey := fmt.Sprintf("%s:%s:%s:%s", direction, tenant, tor, fbs) if !sslice.Contains(newKey) { rpa.FallbackKeys = append(rpa.FallbackKeys, newKey) @@ -462,7 +462,7 @@ func (csvr *CSVReader) LoadActionTimings() (err error) { } at := &ActionTiming{ Id: utils.GenUUID(), - Tag: record[2], + Tag: record[0], Weight: weight, Timing: &RateInterval{ Timing: &RITiming{ diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index bfdde0a1e..8892b6dd6 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -604,7 +604,7 @@ func TestLoadActionTimings(t *testing.T) { atm := csvr.actionsTimings["MORE_MINUTES"][0] expected := &ActionTiming{ Id: atm.Id, - Tag: "ONE_TIME_RUN", + Tag: "MORE_MINUTES", UserBalanceIds: []string{"*out:vdf:minitsboy"}, Timing: &RateInterval{ Timing: &RITiming{ @@ -619,7 +619,7 @@ func TestLoadActionTimings(t *testing.T) { ActionsId: "MINI", } if !reflect.DeepEqual(atm, expected) { - t.Error("Error loading action timing: ", atm) + t.Error("Error loading action timing: ", atm, expected) } } diff --git a/engine/loader_db.go b/engine/loader_db.go index 042fe52f1..6ac24aa2e 100644 --- a/engine/loader_db.go +++ b/engine/loader_db.go @@ -48,6 +48,7 @@ func NewDbReader(storDB LoadStorage, storage DataStorage, tpid string) *DbReader c.dataDb = storage c.tpid = tpid c.actionsTimings = make(map[string][]*ActionTiming) + c.actionsTriggers = make(map[string][]*ActionTrigger) c.ratingPlans = make(map[string]*RatingPlan) c.ratingProfiles = make(map[string]*RatingProfile) return c @@ -180,51 +181,52 @@ func (dbr *DbReader) LoadDestinationRates() (err error) { } func (dbr *DbReader) LoadRatingPlans() error { - drts, err := dbr.storDb.GetTpRatingPlans(dbr.tpid, "") + mpRpls, err := dbr.storDb.GetTpRatingPlans(dbr.tpid, "") if err != nil { return err } - for _, drt := range drts.RatingPlans { - t, exists := dbr.timings[drt.TimingId] - if !exists { - return errors.New(fmt.Sprintf("Could not get timing for tag %v", drt.TimingId)) - } - drt.SetTiming(t) - drs, exists := dbr.destinationRates[drt.DestinationRatesId] - if !exists { - return errors.New(fmt.Sprintf("Could not find destination rate for tag %v", drt.DestinationRatesId)) - } - - plan, exists := dbr.ratingPlans[drts.RatingPlanId] - if !exists { - plan = &RatingPlan{Id: drts.RatingPlanId} - dbr.ratingPlans[drts.RatingPlanId] = plan - } - for _, dr := range drs.DestinationRates { - plan.AddRateInterval(dr.DestinationId, GetRateInterval(drt, dr)) + for tag, rplBnds := range mpRpls { + for _,rplBnd := range rplBnds { + t, exists := dbr.timings[rplBnd.TimingId] + if !exists { + return errors.New(fmt.Sprintf("Could not get timing for tag %v", rplBnd.TimingId)) + } + rplBnd.SetTiming(t) + drs, exists := dbr.destinationRates[rplBnd.DestinationRatesId] + if !exists { + return errors.New(fmt.Sprintf("Could not find destination rate for tag %v", rplBnd.DestinationRatesId)) + } + plan, exists := dbr.ratingPlans[tag] + if !exists { + plan = &RatingPlan{Id: tag} + dbr.ratingPlans[plan.Id] = plan + } + for _, dr := range drs.DestinationRates { + plan.AddRateInterval(dr.DestinationId, GetRateInterval(rplBnd, dr)) + } } } return nil } func (dbr *DbReader) LoadRatingProfiles() error { - mpTpRpfs, err := dbr.storDb.GetTpRatingProfiles(dbr.tpid, "") //map[string]*utils.TPRatingProfile + mpTpRpfs, err := dbr.storDb.GetTpRatingProfiles(&utils.TPRatingProfile{TPid:dbr.tpid}) //map[string]*utils.TPRatingProfile if err != nil { return err } for _, tpRpf := range mpTpRpfs { - rpf := &RatingProfile{Id: tpRpf.RatingProfileId} + rpf := &RatingProfile{Id: tpRpf.KeyId()} for _, tpRa := range tpRpf.RatingPlanActivations { at, err := utils.ParseDate(tpRa.ActivationTime) if err != nil { return errors.New(fmt.Sprintf("Cannot parse activation time from %v", tpRa.ActivationTime)) } - _, exists := dbr.ratingPlans[rpf.Id] + _, exists := dbr.ratingPlans[tpRa.RatingPlanId] if !exists { - if dbExists, err := dbr.dataDb.ExistsData(RATING_PLAN_PREFIX, rpf.Id); err != nil { + if dbExists, err := dbr.dataDb.ExistsData(RATING_PLAN_PREFIX, tpRa.RatingPlanId); err != nil { return err } else if !dbExists { - return errors.New(fmt.Sprintf("Could not load rating plans for tag: %v", rpf.Id)) + return errors.New(fmt.Sprintf("Could not load rating plans for tag: %v", tpRa.RatingPlanId)) } } rpf.RatingPlanActivations = append(rpf.RatingPlanActivations, @@ -234,81 +236,85 @@ func (dbr *DbReader) LoadRatingProfiles() error { FallbackKeys: strings.Split(tpRa.FallbackSubjects,FALLBACK_SEP), }) } - dbr.ratingProfiles[rpf.Id] = rpf + dbr.ratingProfiles[tpRpf.KeyId()] = rpf } return nil } func (dbr *DbReader) LoadRatingPlanByTag(tag string) error { - ratingPlan := &RatingPlan{} - rps, err := dbr.storDb.GetTpRatingPlans(dbr.tpid, tag) - if err != nil || len(rps.RatingPlans) == 0 { + mpRpls, err := dbr.storDb.GetTpRatingPlans(dbr.tpid, tag) + if err != nil || len(mpRpls) == 0 { return fmt.Errorf("No DestRateTimings profile with id %s: %v", tag, err) } - for _, rp := range rps.RatingPlans { - Logger.Debug(fmt.Sprintf("Rating Plan: %v", rp)) - tm, err := dbr.storDb.GetTpTimings(dbr.tpid, rp.TimingId) - Logger.Debug(fmt.Sprintf("Timing: %v", tm)) - if err != nil || len(tm) == 0 { - return fmt.Errorf("No Timings profile with id %s: %v", rp.TimingId, err) - } - rp.SetTiming(tm[rp.TimingId]) - drm, err := dbr.storDb.GetTpDestinationRates(dbr.tpid, rp.DestinationRatesId) - if err != nil || len(drm) == 0 { - return fmt.Errorf("No DestinationRates profile with id %s: %v", rp.DestinationRatesId, err) - } - for _, drate := range drm[rp.DestinationRatesId].DestinationRates { - Logger.Debug(fmt.Sprintf("Destination rate: %v", drate)) - rt, err := dbr.storDb.GetTpRates(dbr.tpid, drate.RateId) - if err != nil || len(rt) == 0 { - return fmt.Errorf("No Rates profile with id %s: %v", drate.RateId, err) + for tag, rplBnds := range mpRpls { + ratingPlan := &RatingPlan{Id: tag} + for _, rp := range rplBnds { + Logger.Debug(fmt.Sprintf("Rating Plan binding: %v", rp)) + tm, err := dbr.storDb.GetTpTimings(dbr.tpid, rp.TimingId) + Logger.Debug(fmt.Sprintf("Timing: %v", tm)) + if err != nil || len(tm) == 0 { + return fmt.Errorf("No Timings profile with id %s: %v", rp.TimingId, err) } - Logger.Debug(fmt.Sprintf("Rate: %v", rt)) - drate.Rate = rt[drate.RateId] - ratingPlan.AddRateInterval(drate.DestinationId, GetRateInterval(rp, drate)) - - dms, err := dbr.storDb.GetTpDestinations(dbr.tpid, drate.DestinationId) - if err != nil { - return err - } else if len(dms) == 0 { - if dbExists, err := dbr.dataDb.ExistsData(DESTINATION_PREFIX, drate.DestinationId); err != nil { - return err - } else if !dbExists { - return fmt.Errorf("Could not get destination for tag %v", drate.DestinationId) + rp.SetTiming(tm[rp.TimingId]) + drm, err := dbr.storDb.GetTpDestinationRates(dbr.tpid, rp.DestinationRatesId) + if err != nil || len(drm) == 0 { + return fmt.Errorf("No DestinationRates profile with id %s: %v", rp.DestinationRatesId, err) + } + for _, drate := range drm[rp.DestinationRatesId].DestinationRates { + Logger.Debug(fmt.Sprintf("Destination rate: %v", drate)) + rt, err := dbr.storDb.GetTpRates(dbr.tpid, drate.RateId) + if err != nil || len(rt) == 0 { + return fmt.Errorf("No Rates profile with id %s: %v", drate.RateId, err) + } + Logger.Debug(fmt.Sprintf("Rate: %v", rt)) + drate.Rate = rt[drate.RateId] + ratingPlan.AddRateInterval(drate.DestinationId, GetRateInterval(rp, drate)) + + dms, err := dbr.storDb.GetTpDestinations(dbr.tpid, drate.DestinationId) + if err != nil { + return err + } else if len(dms) == 0 { + if dbExists, err := dbr.dataDb.ExistsData(DESTINATION_PREFIX, drate.DestinationId); err != nil { + return err + } else if !dbExists { + return fmt.Errorf("Could not get destination for tag %v", drate.DestinationId) + } + continue + } + Logger.Debug(fmt.Sprintf("Tag: %s Destinations: %v", drate.DestinationId, dms)) + for _, destination := range dms { + Logger.Debug(fmt.Sprintf("Destination: %v", destination)) + dbr.dataDb.SetDestination(destination) } - continue - } - Logger.Debug(fmt.Sprintf("Tag: %s Destinations: %v", drate.DestinationId, dms)) - for _, destination := range dms { - Logger.Debug(fmt.Sprintf("Destination: %v", destination)) - dbr.dataDb.SetDestination(destination) } } + if err := dbr.dataDb.SetRatingPlan(ratingPlan); err != nil { + return err + } } - return dbr.dataDb.SetRatingPlan(ratingPlan) + return nil } func (dbr *DbReader) LoadRatingProfileByTag(tag string) error { resultRatingProfile := &RatingProfile{} - mpTpRpfs, err := dbr.storDb.GetTpRatingProfiles(dbr.tpid, tag) //map[string]*utils.TPRatingProfile + mpTpRpfs, err := dbr.storDb.GetTpRatingProfiles(&utils.TPRatingProfile{TPid:dbr.tpid, LoadId:tag}) //map[string]*utils.TPRatingProfile if err != nil || len(mpTpRpfs) == 0 { return fmt.Errorf("No RateProfile with id %s: %v", tag, err) } for _, tpRpf := range mpTpRpfs { Logger.Debug(fmt.Sprintf("Rating profile: %v", tpRpf)) - resultRatingProfile.Id = tpRpf.RatingProfileId - /// + resultRatingProfile.Id = tpRpf.KeyId() for _, tpRa := range tpRpf.RatingPlanActivations { at, err := utils.ParseDate(tpRa.ActivationTime) if err != nil { return errors.New(fmt.Sprintf("Cannot parse activation time from %v", tpRa.ActivationTime)) } - _, exists := dbr.ratingPlans[resultRatingProfile.Id] + _, exists := dbr.ratingPlans[tpRa.RatingPlanId] if !exists { - if dbExists, err := dbr.dataDb.ExistsData(RATING_PLAN_PREFIX, resultRatingProfile.Id); err != nil { + if dbExists, err := dbr.dataDb.ExistsData(RATING_PLAN_PREFIX, tpRa.RatingPlanId); err != nil { return err } else if !dbExists { - return errors.New(fmt.Sprintf("Could not load rating plans for tag: %v", resultRatingProfile.Id)) + return errors.New(fmt.Sprintf("Could not load rating plans for tag: %v", tpRa.RatingPlanId)) } } resultRatingProfile.RatingPlanActivations = append(resultRatingProfile.RatingPlanActivations, @@ -334,9 +340,9 @@ func (dbr *DbReader) LoadActionTimings() (err error) { if !exists { return errors.New(fmt.Sprintf("ActionTiming: Could not load the action for tag: %v", at.ActionsId)) } - t, exists := dbr.timings[atId] + t, exists := dbr.timings[at.TimingId] if !exists { - return errors.New(fmt.Sprintf("ActionTiming: Could not load the timing for tag: %v", atId)) + return errors.New(fmt.Sprintf("ActionTiming: Could not load the timing for tag: %v", at.TimingId)) } actTmg := &ActionTiming{ Id: utils.GenUUID(), @@ -344,6 +350,7 @@ func (dbr *DbReader) LoadActionTimings() (err error) { Weight: at.Weight, Timing: &RateInterval{ Timing: &RITiming{ + Years: t.Years, Months: t.Months, MonthDays: t.MonthDays, WeekDays: t.WeekDays, @@ -382,38 +389,35 @@ func (dbr *DbReader) LoadActionTriggers() (err error) { } func (dbr *DbReader) LoadAccountActions() (err error) { - acs, err := dbr.storDb.GetTpAccountActions(dbr.tpid, "") + acs, err := dbr.storDb.GetTpAccountActions(&utils.TPAccountActions{TPid:dbr.tpid}) if err != nil { return err } for _, aa := range acs { - tag := fmt.Sprintf("%s:%s:%s", aa.Direction, aa.Tenant, aa.Account) - aTriggers, exists := dbr.actionsTriggers[aa.ActionTriggersTag] - if aa.ActionTriggersTag != "" && !exists { - // only return error if there was something there for the tag - return errors.New(fmt.Sprintf("Could not get action triggers for tag %v", aa.ActionTriggersTag)) + aTriggers, exists := dbr.actionsTriggers[aa.ActionTriggersId] + if !exists { + return errors.New(fmt.Sprintf("Could not get action triggers for tag %v", aa.ActionTriggersId)) } ub := &UserBalance{ Type: UB_TYPE_PREPAID, - Id: tag, + Id: aa.KeyId(), ActionTriggers: aTriggers, } dbr.accountActions = append(dbr.accountActions, ub) - - aTimings, exists := dbr.actionsTimings[aa.ActionTimingsTag] + aTimings, exists := dbr.actionsTimings[aa.ActionTimingsId] if !exists { - log.Printf("Could not get action timing for tag %v", aa.ActionTimingsTag) + log.Printf("Could not get action timing for tag %v", aa.ActionTimingsId) // must not continue here } for _, at := range aTimings { - at.UserBalanceIds = append(at.UserBalanceIds, tag) + at.UserBalanceIds = append(at.UserBalanceIds, aa.KeyId()) } } return nil } func (dbr *DbReader) LoadAccountActionsByTag(tag string) error { - accountActions, err := dbr.storDb.GetTpAccountActions(dbr.tpid, tag) + accountActions, err := dbr.storDb.GetTpAccountActions(&utils.TPAccountActions{TPid:dbr.tpid, LoadId:tag}) if err != nil { return err } else if len(accountActions) == 0 { @@ -427,23 +431,23 @@ func (dbr *DbReader) LoadAccountActionsByTag(tag string) error { var actionsIds []string // collects action ids // action timings - if accountAction.ActionTimingsTag != "" { + if accountAction.ActionTimingsId != "" { // get old userBalanceIds var exitingUserBalanceIds []string - existingActionTimings, err := dbr.dataDb.GetActionTimings(accountAction.ActionTimingsTag) + existingActionTimings, err := dbr.dataDb.GetActionTimings(accountAction.ActionTimingsId) if err == nil && len(existingActionTimings) > 0 { // all action timings from a specific tag shuld have the same list of user balances from the first one exitingUserBalanceIds = existingActionTimings[0].UserBalanceIds } - actionTimingsMap, err := dbr.storDb.GetTpActionTimings(dbr.tpid, accountAction.ActionTimingsTag) + actionTimingsMap, err := dbr.storDb.GetTpActionTimings(dbr.tpid, accountAction.ActionTimingsId) if err != nil { return err } else if len(actionTimingsMap) == 0 { - return fmt.Errorf("No ActionTimings with id <%s>", accountAction.ActionTimingsTag) + return fmt.Errorf("No ActionTimings with id <%s>", accountAction.ActionTimingsId) } var actionTimings []*ActionTiming - ats := actionTimingsMap[accountAction.ActionTimingsTag] + ats := actionTimingsMap[accountAction.ActionTimingsId] for _, at := range ats { existsAction, err := dbr.storDb.ExistsTPActions(dbr.tpid, at.ActionsId) if err != nil { @@ -451,16 +455,16 @@ func (dbr *DbReader) LoadAccountActionsByTag(tag string) error { } else if !existsAction { return fmt.Errorf("No Action with id <%s>", at.ActionsId) } - timingsMap, err := dbr.storDb.GetTpTimings(dbr.tpid, accountAction.ActionTimingsTag) + timingsMap, err := dbr.storDb.GetTpTimings(dbr.tpid, accountAction.ActionTimingsId) if err != nil { return err } else if len(timingsMap) == 0 { - return fmt.Errorf("No Timing with id <%s>", accountAction.ActionTimingsTag) + return fmt.Errorf("No Timing with id <%s>", accountAction.ActionTimingsId) } - t := timingsMap[accountAction.ActionTimingsTag] + t := timingsMap[accountAction.ActionTimingsId] actTmg := &ActionTiming{ Id: utils.GenUUID(), - Tag: accountAction.ActionTimingsTag, + Tag: accountAction.ActionTimingsId, Weight: at.Weight, Timing: &RateInterval{ Timing: &RITiming{ @@ -489,7 +493,7 @@ func (dbr *DbReader) LoadAccountActionsByTag(tag string) error { } // write action timings - err = dbr.dataDb.SetActionTimings(accountAction.ActionTimingsTag, actionTimings) + err = dbr.dataDb.SetActionTimings(accountAction.ActionTimingsId, actionTimings) if err != nil { return err } @@ -498,8 +502,8 @@ func (dbr *DbReader) LoadAccountActionsByTag(tag string) error { // action triggers var actionTriggers ActionTriggerPriotityList //ActionTriggerPriotityList []*ActionTrigger - if accountAction.ActionTriggersTag != "" { - apiAtrsMap, err := dbr.storDb.GetTpActionTriggers(dbr.tpid, accountAction.ActionTriggersTag) + if accountAction.ActionTriggersId != "" { + apiAtrsMap, err := dbr.storDb.GetTpActionTriggers(dbr.tpid, accountAction.ActionTriggersId) if err != nil { return err } @@ -519,7 +523,7 @@ func (dbr *DbReader) LoadAccountActionsByTag(tag string) error { } atrsMap[key] = atrs } - actionTriggers = atrsMap[accountAction.ActionTriggersTag] + actionTriggers = atrsMap[accountAction.ActionTriggersId] // collect action ids from triggers for _, atr := range actionTriggers { actionsIds = append(actionsIds, atr.ActionsId) diff --git a/engine/loader_helpers.go b/engine/loader_helpers.go index 7202ca003..68a47d1dc 100644 --- a/engine/loader_helpers.go +++ b/engine/loader_helpers.go @@ -106,20 +106,20 @@ func NewTiming(timingInfo ...string) (rt *utils.TPTiming) { return } -func NewRatingPlan(timing *utils.TPTiming, weight string) (drt *utils.RatingPlan) { +func NewRatingPlan(timing *utils.TPTiming, weight string) (drt *utils.TPRatingPlanBinding) { w, err := strconv.ParseFloat(weight, 64) if err != nil { log.Printf("Error parsing weight unit from: %v", weight) return } - drt = &utils.RatingPlan{ + drt = &utils.TPRatingPlanBinding{ Weight: w, } drt.SetTiming(timing) return } -func GetRateInterval(rpl *utils.RatingPlan, dr *utils.DestinationRate) (i *RateInterval) { +func GetRateInterval(rpl *utils.TPRatingPlanBinding, dr *utils.DestinationRate) (i *RateInterval) { i = &RateInterval{ Timing: &RITiming{ Years: rpl.Timing().Years, diff --git a/engine/storage_interface.go b/engine/storage_interface.go index 405f53bec..8155b2e76 100644 --- a/engine/storage_interface.go +++ b/engine/storage_interface.go @@ -105,7 +105,7 @@ type LoadStorage interface { ExistsTPTiming(string, string) (bool, error) GetTPTiming(string, string) (*utils.TPTiming, error) GetTPTimingIds(string) ([]string, error) - RemTPData(string, string, string) error + RemTPData(string, string, ...string) error SetTPDestination(string, *Destination) error ExistsTPDestination(string, string) (bool, error) GetTPDestination(string, string) (*Destination, error) @@ -119,7 +119,7 @@ type LoadStorage interface { GetTPDestinationRate(string, string) (*utils.TPDestinationRate, error) GetTPDestinationRateIds(string) ([]string, error) ExistsTPRatingPlan(string, string) (bool, error) - SetTPRatingPlans(string, map[string][]*utils.RatingPlan) error + SetTPRatingPlans(string, map[string][]*utils.TPRatingPlanBinding) error GetTPRatingPlan(string, string) (*utils.TPRatingPlan, error) GetTPRatingPlanIds(string) ([]string, error) ExistsTPRatingProfile(string, string) (bool, error) @@ -145,12 +145,12 @@ type LoadStorage interface { GetTpTimings(string, string) (map[string]*utils.TPTiming, error) GetTpRates(string, string) (map[string]*utils.TPRate, error) GetTpDestinationRates(string, string) (map[string]*utils.TPDestinationRate, error) - GetTpRatingPlans(string, string) (*utils.TPRatingPlan, error) - GetTpRatingProfiles(string, string) (map[string]*utils.TPRatingProfile, error) + GetTpRatingPlans(string, string) (map[string][]*utils.TPRatingPlanBinding, error) + GetTpRatingProfiles(*utils.TPRatingProfile) (map[string]*utils.TPRatingProfile, error) GetTpActions(string, string) (map[string][]*Action, error) GetTpActionTimings(string, string) (map[string][]*utils.TPActionTiming, error) GetTpActionTriggers(string, string) (map[string][]*utils.TPActionTrigger, error) - GetTpAccountActions(string, string) (map[string]*AccountAction, error) + GetTpAccountActions(*utils.TPAccountActions) (map[string]*utils.TPAccountActions, error) } type Marshaler interface { diff --git a/engine/storage_sql.go b/engine/storage_sql.go index a3f596d08..753760fb5 100644 --- a/engine/storage_sql.go +++ b/engine/storage_sql.go @@ -119,8 +119,16 @@ func (self *SQLStorage) GetTPTimingIds(tpid string) ([]string, error) { -func (self *SQLStorage) RemTPData(table, tpid, tag string) error { - q := fmt.Sprintf("DELETE FROM %s WHERE tpid='%s' AND tag='%s'", table, tpid, tag) +func (self *SQLStorage) RemTPData(table, tpid string, args ...string) error { + q := fmt.Sprintf("DELETE FROM %s WHERE tpid='%s' AND tag='%s'", table, tpid, args[0]) + switch table { + case utils.TBL_TP_RATE_PROFILES: + q = fmt.Sprintf("DELETE FROM %s WHERE tpid='%s' AND loadid='%s' AND tenant='%s' AND tor='%s' AND direction='%s' AND subject='%s'", + table, tpid, args[0], args[1], args[2], args[3], args[4]) + case utils.TBL_TP_ACCOUNT_ACTIONS: + q = fmt.Sprintf("DELETE FROM %s WHERE tpid='%s' AND loadid='%s' AND tenant='%s' AND account='%s' AND direction='%s'", + table, tpid, args[0], args[1], args[2], args[3]) + } if _, err := self.Db.Exec(q); err != nil { return err } @@ -375,7 +383,7 @@ func (self *SQLStorage) ExistsTPRatingPlan(tpid, drtId string) (bool, error) { return exists, nil } -func (self *SQLStorage) SetTPRatingPlans(tpid string, drts map[string][]*utils.RatingPlan) error { +func (self *SQLStorage) SetTPRatingPlans(tpid string, drts map[string][]*utils.TPRatingPlanBinding) error { if len(drts) == 0 { return nil //Nothing to set } @@ -414,7 +422,7 @@ func (self *SQLStorage) GetTPRatingPlan(tpid, drtId string) (*utils.TPRatingPlan if err != nil { return nil, err } - drt.RatingPlans = append(drt.RatingPlans, &utils.RatingPlan{DestinationRatesId:drTag, TimingId:timingTag, Weight:weight}) + drt.RatingPlanBindings = append(drt.RatingPlanBindings, &utils.TPRatingPlanBinding{DestinationRatesId:drTag, TimingId:timingTag, Weight:weight}) } if i == 0 { return nil, nil @@ -465,25 +473,25 @@ func (self *SQLStorage) SetTPRatingProfiles(tpid string, rps map[string]*utils.T if i != 0 { //Consecutive values after the first will be prefixed with "," as separator vals += "," } - vals += fmt.Sprintf("('%s', '%s', '%s', '%s', '%s', '%s', '%s','%s','%s')", tpid, rp.RatingProfileId, rp.Tenant, rp.TOR, rp.Direction, + vals += fmt.Sprintf("('%s', '%s', '%s', '%s', '%s', '%s', '%s','%s','%s')", tpid, rp.LoadId, rp.Tenant, rp.TOR, rp.Direction, rp.Subject, rpa.ActivationTime, rpa.RatingPlanId, rpa.FallbackSubjects) i++ } } - qry := fmt.Sprintf("INSERT INTO %s (tpid,tag,tenant,tor,direction,subject,activation_time,rating_plan_tag,fallback_subjects) VALUES %s ON DUPLICATE KEY UPDATE fallback_subjects=values(fallback_subjects)", utils.TBL_TP_RATE_PROFILES, vals) + qry := fmt.Sprintf("INSERT INTO %s (tpid,loadid,tenant,tor,direction,subject,activation_time,rating_plan_tag,fallback_subjects) VALUES %s ON DUPLICATE KEY UPDATE fallback_subjects=values(fallback_subjects)", utils.TBL_TP_RATE_PROFILES, vals) if _, err := self.Db.Exec(qry); err != nil { return err } return nil } -func (self *SQLStorage) GetTPRatingProfile(tpid, rpId string) (*utils.TPRatingProfile, error) { - rows, err := self.Db.Query(fmt.Sprintf("SELECT tenant,tor,direction,subject,activation_time,rating_plan_tag,fallback_subjects FROM %s WHERE tpid='%s' AND tag='%s'", utils.TBL_TP_RATE_PROFILES, tpid, rpId)) +func (self *SQLStorage) GetTPRatingProfile(tpid, loadId string) (*utils.TPRatingProfile, error) { + rows, err := self.Db.Query(fmt.Sprintf("SELECT tenant,tor,direction,subject,activation_time,rating_plan_tag,fallback_subjects FROM %s WHERE tpid='%s' AND loadid='%s'", utils.TBL_TP_RATE_PROFILES, tpid, loadId)) if err != nil { return nil, err } defer rows.Close() - rp := &utils.TPRatingProfile{TPid: tpid, RatingProfileId: rpId} + rp := &utils.TPRatingProfile{TPid: tpid, LoadId: loadId} i := 0 for rows.Next() { i++ //Keep here a reference so we know we got at least one result @@ -507,7 +515,7 @@ func (self *SQLStorage) GetTPRatingProfile(tpid, rpId string) (*utils.TPRatingPr } func (self *SQLStorage) GetTPRatingProfileIds(filters *utils.AttrTPRatingProfileIds) ([]string, error) { - qry := fmt.Sprintf("SELECT DISTINCT tag FROM %s where tpid='%s'", utils.TBL_TP_RATE_PROFILES, filters.TPid) + qry := fmt.Sprintf("SELECT DISTINCT loadid FROM %s where tpid='%s'", utils.TBL_TP_RATE_PROFILES, filters.TPid) if filters.Tenant != "" { qry += fmt.Sprintf(" AND tenant='%s'", filters.Tenant) } @@ -770,15 +778,15 @@ func (self *SQLStorage) SetTPAccountActions(tpid string, aa map[string]*utils.TP } vals := "" i := 0 - for aaId, aActs := range aa { + for _, aActs := range aa { if i != 0 { //Consecutive values after the first will be prefixed with "," as separator vals += "," } vals += fmt.Sprintf("('%s','%s','%s','%s','%s','%s','%s')", - tpid, aaId, aActs.Tenant, aActs.Account, aActs.Direction, aActs.ActionTimingsId, aActs.ActionTriggersId) + tpid, aActs.LoadId, aActs.Tenant, aActs.Account, aActs.Direction, aActs.ActionTimingsId, aActs.ActionTriggersId) i++ } - qry := fmt.Sprintf("INSERT INTO %s (tpid, tag, tenant, account, direction, action_timings_tag, action_triggers_tag) VALUES %s ON DUPLICATE KEY UPDATE action_timings_tag=values(action_timings_tag), action_triggers_tag=values(action_triggers_tag)", utils.TBL_TP_ACCOUNT_ACTIONS, vals) + qry := fmt.Sprintf("INSERT INTO %s (tpid, loadid, tenant, account, direction, action_timings_tag, action_triggers_tag) VALUES %s ON DUPLICATE KEY UPDATE action_timings_tag=values(action_timings_tag), action_triggers_tag=values(action_triggers_tag)", utils.TBL_TP_ACCOUNT_ACTIONS, vals) if _, err := self.Db.Exec(qry); err != nil { return err } @@ -786,7 +794,7 @@ func (self *SQLStorage) SetTPAccountActions(tpid string, aa map[string]*utils.TP } func (self *SQLStorage) GetTPAccountActionIds(tpid string) ([]string, error) { - rows, err := self.Db.Query(fmt.Sprintf("SELECT DISTINCT tag FROM %s where tpid='%s'", utils.TBL_TP_ACCOUNT_ACTIONS, tpid)) + rows, err := self.Db.Query(fmt.Sprintf("SELECT DISTINCT loadid FROM %s where tpid='%s'", utils.TBL_TP_ACCOUNT_ACTIONS, tpid)) if err != nil { return nil, err } @@ -1085,8 +1093,8 @@ func (self *SQLStorage) GetTpTimings(tpid, tag string) (map[string]*utils.TPTimi return tms, nil } -func (self *SQLStorage) GetTpRatingPlans(tpid, tag string) (*utils.TPRatingPlan, error) { - rts := &utils.TPRatingPlan{RatingPlanId: tag} +func (self *SQLStorage) GetTpRatingPlans(tpid, tag string) (map[string][]*utils.TPRatingPlanBinding, error) { + rpbns := make(map[string][]*utils.TPRatingPlanBinding) q := fmt.Sprintf("SELECT * FROM %s WHERE tpid='%s'", utils.TBL_TP_RATING_PLANS, tpid) if tag != "" { q += fmt.Sprintf(" AND tag='%s'", tag) @@ -1103,44 +1111,62 @@ func (self *SQLStorage) GetTpRatingPlans(tpid, tag string) (*utils.TPRatingPlan, if err := rows.Scan(&id, &tpid, &tag, &destination_rates_tag, &timings_tag, &weight); err != nil { return nil, err } - rt := &utils.RatingPlan{ + rpb := &utils.TPRatingPlanBinding{ DestinationRatesId: destination_rates_tag, - Weight: weight, TimingId: timings_tag, + Weight: weight, + } + if rpBnLst,exists := rpbns[tag]; exists { + rpBnLst = append(rpBnLst, rpb) + } else { // New + rpbns[tag] = []*utils.TPRatingPlanBinding{rpb} } - rts.RatingPlans = append(rts.RatingPlans, rt) } - return rts, nil + return rpbns, nil } -func (self *SQLStorage) GetTpRatingProfiles(tpid, tag string) (map[string]*utils.TPRatingProfile, error) { - rpfs := make(map[string]*utils.TPRatingProfile) - q := fmt.Sprintf("SELECT tag,tenant,tor,direction,subject,activation_time,rating_plan_tag,fallback_subjects FROM %s WHERE tpid='%s'", - utils.TBL_TP_RATE_PROFILES, tpid) - if tag != "" { - q += fmt.Sprintf(" AND tag='%s'", tag) +func (self *SQLStorage) GetTpRatingProfiles(qryRpf *utils.TPRatingProfile) (map[string]*utils.TPRatingProfile, error) { + q := fmt.Sprintf("SELECT loadid,tenant,tor,direction,subject,activation_time,rating_plan_tag,fallback_subjects FROM %s WHERE tpid='%s'", + utils.TBL_TP_RATE_PROFILES, qryRpf.TPid) + if len(qryRpf.LoadId) != 0 { + q += fmt.Sprintf(" AND loadid='%s'", qryRpf.LoadId) } + if len(qryRpf.Tenant) != 0 { + q += fmt.Sprintf(" AND tenant='%s'", qryRpf.Tenant) + } + if len(qryRpf.TOR) != 0 { + q += fmt.Sprintf(" AND tor='%s'", qryRpf.TOR) + } + if len(qryRpf.Direction) != 0 { + q += fmt.Sprintf(" AND direction='%s'", qryRpf.Direction) + } + if len(qryRpf.Subject) != 0 { + q += fmt.Sprintf(" AND subject='%s'", qryRpf.Subject) + } rows, err := self.Db.Query(q) if err != nil { return nil, err } defer rows.Close() + rpfs := make(map[string]*utils.TPRatingProfile) for rows.Next() { - var rcvTag, tenant, tor, direction, subject, fallback_subjects, rating_plan_tag, activation_time string - if err := rows.Scan(&rcvTag, &tenant, &tor, &direction, &subject, &activation_time, &rating_plan_tag, &fallback_subjects); err != nil { + var rcvLoadId, tenant, tor, direction, subject, fallback_subjects, rating_plan_tag, activation_time string + if err := rows.Scan(&rcvLoadId, &tenant, &tor, &direction, &subject, &activation_time, &rating_plan_tag, &fallback_subjects); err != nil { return nil, err } - if _,ok := rpfs[rcvTag]; !ok { - rpfs[rcvTag] = &utils.TPRatingProfile{TPid: tpid, RatingProfileId: rcvTag, Tenant: tenant, TOR: tor, Direction:direction, Subject:subject, - RatingPlanActivations: []*utils.TPRatingActivation{ - &utils.TPRatingActivation{ActivationTime:activation_time, RatingPlanId:rating_plan_tag, FallbackSubjects:fallback_subjects}}} - } else { - rpfs[rcvTag].RatingPlanActivations = append( rpfs[rcvTag].RatingPlanActivations, + rp := &utils.TPRatingProfile{TPid: qryRpf.TPid, LoadId: rcvLoadId, Tenant: tenant, TOR: tor, Direction:direction, Subject:subject} + if existingRp,has := rpfs[rp.KeyId()]; !has { + rp.RatingPlanActivations = []*utils.TPRatingActivation{ + &utils.TPRatingActivation{ActivationTime:activation_time, RatingPlanId:rating_plan_tag, FallbackSubjects:fallback_subjects}} + rpfs[rp.KeyId()] = rp + } else { // Exists, update + existingRp.RatingPlanActivations = append( existingRp.RatingPlanActivations, &utils.TPRatingActivation{ActivationTime:activation_time, RatingPlanId:rating_plan_tag, FallbackSubjects:fallback_subjects} ) } } return rpfs, nil } + func (self *SQLStorage) GetTpActions(tpid, tag string) (map[string][]*Action, error) { as := make(map[string][]*Action) q := fmt.Sprintf("SELECT * FROM %s WHERE tpid='%s'", utils.TBL_TP_ACTIONS, tpid) @@ -1168,6 +1194,7 @@ func (self *SQLStorage) GetTpActions(tpid, tag string) (map[string][]*Action, er ExtraParameters: extra_parameters, ExpirationString: expirationDate, Balance: &Balance{ + Uuid: utils.GenUUID(), Value: units, Weight: balance_weight, RateSubject: rating_subject, @@ -1239,29 +1266,41 @@ func (self *SQLStorage) GetTpActionTriggers(tpid, tag string) (map[string][]*uti return ats, nil } -func (self *SQLStorage) GetTpAccountActions(tpid, tag string) (map[string]*AccountAction, error) { - q := fmt.Sprintf("SELECT tag, tenant, account, direction, action_timings_tag, action_triggers_tag FROM %s WHERE tpid='%s'", utils.TBL_TP_ACCOUNT_ACTIONS, tpid) - if tag != "" { - q += fmt.Sprintf(" AND tag='%s'", tag) +func (self *SQLStorage) GetTpAccountActions(aaFltr *utils.TPAccountActions) (map[string]*utils.TPAccountActions, error) { + q := fmt.Sprintf("SELECT loadid, tenant, account, direction, action_timings_tag, action_triggers_tag FROM %s WHERE tpid='%s'", utils.TBL_TP_ACCOUNT_ACTIONS, aaFltr.TPid) + if len(aaFltr.LoadId) != 0 { + q += fmt.Sprintf(" AND loadid='%s'", aaFltr.LoadId) + } + if len(aaFltr.Tenant) != 0 { + q += fmt.Sprintf(" AND tenant='%s'", aaFltr.Tenant) + } + if len(aaFltr.Account) != 0 { + q += fmt.Sprintf(" AND account='%s'", aaFltr.Account) + } + if len(aaFltr.Direction) != 0 { + q += fmt.Sprintf(" AND direction='%s'", aaFltr.Direction) } rows, err := self.Db.Query(q) if err != nil { return nil, err } defer rows.Close() - aa := make(map[string]*AccountAction) + aa := make(map[string]*utils.TPAccountActions) for rows.Next() { - var aaId, tenant, account, direction, action_timings_tag, action_triggers_tag string - if err := rows.Scan(&aaId, &tenant, &account, &direction, &action_timings_tag, &action_triggers_tag); err != nil { + var aaLoadId, tenant, account, direction, action_timings_tag, action_triggers_tag string + if err := rows.Scan(&aaLoadId, &tenant, &account, &direction, &action_timings_tag, &action_triggers_tag); err != nil { return nil, err } - aa[aaId] = &AccountAction{ - Tenant: tenant, - Account: account, - Direction: direction, - ActionTimingsTag: action_timings_tag, - ActionTriggersTag: action_triggers_tag, + aacts := &utils.TPAccountActions{ + TPid: aaFltr.TPid, + LoadId: aaLoadId, + Tenant: tenant, + Account: account, + Direction: direction, + ActionTimingsId: action_timings_tag, + ActionTriggersId: action_triggers_tag, } + aa[aacts.KeyId()] = aacts } return aa, nil } diff --git a/engine/tpimporter_csv.go b/engine/tpimporter_csv.go index 6e7e180a0..56b4ca0bc 100644 --- a/engine/tpimporter_csv.go +++ b/engine/tpimporter_csv.go @@ -210,14 +210,14 @@ func (self *TPCSVImporter) importRatingPlans(fn string) error { } continue } - drt := []*utils.RatingPlan{ - &utils.RatingPlan{ + drt := []*utils.TPRatingPlanBinding{ + &utils.TPRatingPlanBinding{ DestinationRatesId: record[1], Weight: weight, TimingId: record[2], }, } - if err := self.StorDb.SetTPRatingPlans(self.TPid, map[string][]*utils.RatingPlan{record[0]: drt}); err != nil { + if err := self.StorDb.SetTPRatingPlans(self.TPid, map[string][]*utils.TPRatingPlanBinding{record[0]: drt}); err != nil { if self.Verbose { log.Printf("Ignoring line %d, storDb operational error: <%s> ", lineNr, err.Error()) } @@ -252,12 +252,12 @@ func (self *TPCSVImporter) importRatingProfiles(fn string) error { } continue } - rpTag := "TPCSV" //Autogenerate rating profile id + loadId := "TPCSV" //Autogenerate rating profile id if self.ImportId != "" { - rpTag += "_" + self.ImportId + loadId += "_" + self.ImportId } rp := &utils.TPRatingProfile{ - RatingProfileId:rpTag, + LoadId: loadId, Tenant: tenant, TOR: tor, Direction: direction, @@ -265,7 +265,7 @@ func (self *TPCSVImporter) importRatingProfiles(fn string) error { RatingPlanActivations: []*utils.TPRatingActivation{ &utils.TPRatingActivation{ ActivationTime: record[4], RatingPlanId: ratingPlanTag, FallbackSubjects: fallbacksubject}}, } - if err := self.StorDb.SetTPRatingProfiles(self.TPid, map[string]*utils.TPRatingProfile{rpTag: rp}); err != nil { + if err := self.StorDb.SetTPRatingProfiles(self.TPid, map[string]*utils.TPRatingProfile{rp.KeyId(): rp}); err != nil { if self.Verbose { log.Printf("Ignoring line %d, storDb operational error: <%s> ", lineNr, err.Error()) } @@ -441,14 +441,13 @@ func (self *TPCSVImporter) importAccountActions(fn string) error { continue } tenant, account, direction, actionTimingsTag, actionTriggersTag := record[0], record[1], record[2], record[3], record[4] - tag := "TPCSV" //Autogenerate account actions profile id + loadId := "TPCSV" //Autogenerate account actions profile id if self.ImportId != "" { - tag += "_" + self.ImportId - } - aa := map[string]*utils.TPAccountActions{ - tag: &utils.TPAccountActions{TPid: self.TPid, AccountActionsId: tag, Tenant: tenant, Account: account, Direction: direction, - ActionTimingsId: actionTimingsTag, ActionTriggersId: actionTriggersTag}, + loadId += "_" + self.ImportId } + tpaa := &utils.TPAccountActions{TPid: self.TPid, LoadId: loadId, Tenant: tenant, Account: account, Direction: direction, + ActionTimingsId: actionTimingsTag, ActionTriggersId: actionTriggersTag} + aa := map[string]*utils.TPAccountActions{tpaa.KeyId(): tpaa} if err := self.StorDb.SetTPAccountActions(self.TPid, aa); err != nil { if self.Verbose { log.Printf("Ignoring line %d, storDb operational error: <%s> ", lineNr, err.Error()) diff --git a/utils/apitpdata.go b/utils/apitpdata.go index 3710c5545..b4f5dfee8 100644 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -20,6 +20,7 @@ package utils import ( "time" + "fmt" ) // This file deals with tp_* data definition @@ -103,33 +104,37 @@ type TPTiming struct { type TPRatingPlan struct { TPid string // Tariff plan id RatingPlanId string // RatingPlan profile id - RatingPlans []*RatingPlan // Set of destinationid-rateid bindings + RatingPlanBindings []*TPRatingPlanBinding // Set of destinationid-rateid bindings } -type RatingPlan struct { +type TPRatingPlanBinding struct { DestinationRatesId string // The DestinationRate identity TimingId string // The timing identity Weight float64 // Binding priority taken into consideration when more DestinationRates are active on a time slot timing *TPTiming // Not exporting it via JSON } -func(self *RatingPlan) SetTiming(tm *TPTiming) { +func(self *TPRatingPlanBinding) SetTiming(tm *TPTiming) { self.timing = tm } -func(self *RatingPlan) Timing() *TPTiming { +func(self *TPRatingPlanBinding) Timing() *TPTiming { return self.timing } type TPRatingProfile struct { TPid string // Tariff plan id - RatingProfileId string // RatingProfile id + LoadId string // Gives ability to load specific RatingProfile based on load identifier, hence being able to keep history also in stordb Tenant string // Tenant's Id TOR string // TypeOfRecord Direction string // Traffic direction, OUT is the only one supported for now Subject string // Rating subject, usually the same as account RatingPlanActivations []*TPRatingActivation // Activate rate profiles at specific time } +// Used as key in nosql db (eg: redis) +func(self *TPRatingProfile) KeyId() string { + return fmt.Sprintf("%s:%s:%s:%s", self.Direction, self.Tenant, self.TOR, self.Subject) +} type TPRatingActivation struct { ActivationTime string // Time when this profile will become active, defined as unix epoch time @@ -156,7 +161,7 @@ type TPAction struct { BalanceType string // Type of balance the action will operate on Direction string // Balance direction Units float64 // Number of units to add/deduct - ExpiryTime string // Time when the units will expire\ + ExpiryTime string // Time when the units will expire DestinationId string // Destination profile id RatingSubject string // Reference a rate subject defined in RatingProfiles BalanceWeight float64 // Balance weight @@ -195,13 +200,17 @@ type TPActionTrigger struct { type TPAccountActions struct { TPid string // Tariff plan id - AccountActionsId string // AccountActions id, used to group actions on a load + LoadId string // LoadId, used to group actions on a load Tenant string // Tenant's Id Account string // Account name Direction string // Traffic direction ActionTimingsId string // Id of ActionTimings profile to use ActionTriggersId string // Id of ActionTriggers profile to use } +// Returns the id used in some nosql dbs (eg: redis) +func(self *TPAccountActions) KeyId() string { + return fmt.Sprintf("%s:%s:%s", self.Direction, self.Tenant, self.Account) +} // Data used to do remote cache reloads via api