diff --git a/apier/tpaccountactions.go b/apier/tpaccountactions.go index aefb74753..c3cfd95ac 100644 --- a/apier/tpaccountactions.go +++ b/apier/tpaccountactions.go @@ -147,7 +147,7 @@ func (self *ApierV1) RemTPAccountActions(attrs AttrGetTPAccountActions, reply *s if err := aa.SetAccountActionId(attrs.AccountActionsId); err != nil { return err } - if err := self.StorDb.RemTPData(utils.TBL_TP_ACCOUNT_ACTIONS, aa.Tpid, aa.Loadid, aa.Tenant, aa.Account, aa.Direction); err != nil { + if err := self.StorDb.RemTPData(utils.TBL_TP_ACCOUNT_ACTIONS, aa.Tpid, aa.Loadid, aa.Direction, aa.Tenant, aa.Account); err != nil { return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) } else { *reply = "OK" diff --git a/apier/tpratingprofiles.go b/apier/tpratingprofiles.go index 3d156fb3c..0d3db485d 100644 --- a/apier/tpratingprofiles.go +++ b/apier/tpratingprofiles.go @@ -24,6 +24,7 @@ import ( "errors" "fmt" + "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/utils" ) @@ -39,13 +40,13 @@ func (self *ApierV1) SetTPRatingProfile(attrs utils.TPRatingProfile, reply *stri return nil } -type AttrGetTPRatingProfile struct { +type AttrGetTPRatingProfileByLoadId struct { TPid string // Tariff plan id LoadId string // RatingProfile id } // Queries specific RatingProfile on tariff plan -func (self *ApierV1) GetTPRatingProfiles(attrs utils.TPRatingProfile, reply *[]*utils.TPRatingProfile) error { +func (self *ApierV1) GetTPRatingProfilesByLoadId(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") @@ -91,12 +92,67 @@ func (self *ApierV1) GetTPRatingProfileLoadIds(attrs utils.AttrTPRatingProfileId return nil } -// Removes specific RatingProfile on Tariff plan -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 +type AttrGetTPRatingProfiles struct { + TPid string // Tariff plan id + RatingProfilesId string // RatingProfile id +} + +// Queries specific RatingProfile on tariff plan +func (self *ApierV1) GetTPRatingProfiles(attrs AttrGetTPRatingProfiles, reply *utils.TPRatingProfile) error { + if missing := utils.MissingStructFields(&attrs, []string{"TPid", "RatingProfilesId"}); 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.LoadId, attrs.Tenant, attrs.Category, attrs.Direction, attrs.Subject); err != nil { + tmpRpf := &utils.TPRatingProfile{TPid: attrs.TPid} + if err := tmpRpf.SetRatingProfilesId(attrs.RatingProfilesId); err != nil { + return err + } + if rpfs, err := self.StorDb.GetTpRatingProfiles(tmpRpf); err != nil { + return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) + } else if len(rpfs) == 0 { + return errors.New(utils.ERR_NOT_FOUND) + } else { + rpf := rpfs[tmpRpf.KeyId()] + tpdc := utils.TPRatingProfile{ + TPid: attrs.TPid, + RatingPlanActivations: rpf.RatingPlanActivations, + } + if err := tpdc.SetRatingProfilesId(attrs.RatingProfilesId); err != nil { + return err + } + *reply = tpdc + } + return nil +} + +type AttrGetTPRatingProfileIds struct { + TPid string // Tariff plan id +} + +// Queries RatingProfiles identities on specific tariff plan. +func (self *ApierV1) GetTPRatingProfileIds(attrs AttrGetTPRatingProfileIds, 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) + } + if ids, err := self.StorDb.GetTPTableIds(attrs.TPid, utils.TBL_TP_RATE_PROFILES, utils.TPDistinctIds{"loadid", "direction", "tenant", "category", "subject"}, nil); err != nil { + return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) + } else if ids == nil { + return errors.New(utils.ERR_NOT_FOUND) + } else { + *reply = ids + } + return nil +} + +// Removes specific RatingProfiles on Tariff plan +func (self *ApierV1) RemTPRatingProfiles(attrs AttrGetTPRatingProfiles, reply *string) error { + if missing := utils.MissingStructFields(&attrs, []string{"TPid", "RatingProfilesId"}); len(missing) != 0 { //Params missing + return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) + } + tmpRpf := engine.TpRatingProfile{} + if err := tmpRpf.SetRatingProfileId(attrs.RatingProfilesId); err != nil { + return err + } + if err := self.StorDb.RemTPData(utils.TBL_TP_RATE_PROFILES, attrs.TPid, tmpRpf.Loadid, tmpRpf.Direction, tmpRpf.Tenant, tmpRpf.Category, tmpRpf.Subject); err != nil { return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) } else { *reply = "OK" diff --git a/engine/models.go b/engine/models.go index 5c95b2321..63d79ec69 100644 --- a/engine/models.go +++ b/engine/models.go @@ -76,6 +76,32 @@ type TpRatingPlan struct { Weight float64 } +type TpRatingProfile struct { + Tbid int64 `gorm:"primary_key:yes"` + Tpid string + Loadid string + Direction string + Tenant string + Category string + Subject string + ActivationTime string + RatingPlanId string + FallbackSubjects string +} + +func (rpf *TpRatingProfile) SetRatingProfileId(id string) error { + ids := strings.Split(id, utils.TP_ID_SEP) + if len(ids) != 5 { + return fmt.Errorf("Wrong TP Rating Profile Id: %s", id) + } + rpf.Loadid = ids[0] + rpf.Direction = ids[1] + rpf.Tenant = ids[2] + rpf.Category = ids[3] + rpf.Subject = ids[4] + return nil +} + type TpLcrRules struct { Tbid int64 `gorm:"primary_key:yes"` Tpid string @@ -152,7 +178,7 @@ type TpAccountAction struct { func (aa *TpAccountAction) SetAccountActionId(id string) error { ids := strings.Split(id, utils.TP_ID_SEP) if len(ids) != 4 { - return fmt.Errorf("Wrong TP Account Action Id!") + return fmt.Errorf("Wrong TP Account Action Id: %s", id) } aa.Loadid = ids[0] aa.Direction = ids[1] diff --git a/engine/storage_sql.go b/engine/storage_sql.go index 8cab8a8fb..0fde5607a 100644 --- a/engine/storage_sql.go +++ b/engine/storage_sql.go @@ -163,10 +163,10 @@ func (self *SQLStorage) RemTPData(table, tpid string, args ...string) error { q := fmt.Sprintf("DELETE FROM %s WHERE tpid='%s' AND id='%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 category='%s' AND direction='%s' AND subject='%s'", + q = fmt.Sprintf("DELETE FROM %s WHERE tpid='%s' AND loadid='%s' AND direction='%s' AND tenant='%s' AND category='%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'", + q = fmt.Sprintf("DELETE FROM %s WHERE tpid='%s' AND loadid='%s' AND direction='%s' AND tenant='%s' AND account='%s'", table, tpid, args[0], args[1], args[2], args[3]) case utils.TBL_TP_DERIVED_CHARGERS: q = fmt.Sprintf("DELETE FROM %s WHERE tpid='%s' AND loadid='%s' AND direction='%s' AND tenant='%s' AND category='%s' AND account='%s' AND subject='%s'", @@ -264,28 +264,35 @@ func (self *SQLStorage) SetTPRatingPlans(tpid string, drts map[string][]*utils.T return nil } -func (self *SQLStorage) SetTPRatingProfiles(tpid string, rps map[string]*utils.TPRatingProfile) error { - if len(rps) == 0 { +func (self *SQLStorage) SetTPRatingProfiles(tpid string, rpfs map[string]*utils.TPRatingProfile) error { + if len(rpfs) == 0 { return nil //Nothing to set } - var buffer bytes.Buffer - buffer.WriteString(fmt.Sprintf("INSERT INTO %s (tpid,loadid,direction,tenant,category,subject,activation_time,rating_plan_id,fallback_subjects) VALUES ", - utils.TBL_TP_RATE_PROFILES)) - i := 0 - for _, rp := range rps { - for _, rpa := range rp.RatingPlanActivations { - if i != 0 { //Consecutive values after the first will be prefixed with "," as separator - buffer.WriteRune(',') - } - buffer.WriteString(fmt.Sprintf("('%s', '%s', '%s', '%s', '%s', '%s', '%s','%s','%s')", tpid, rp.LoadId, rp.Direction, rp.Tenant, rp.Category, - rp.Subject, rpa.ActivationTime, rpa.RatingPlanId, rpa.FallbackSubjects)) - i++ + tx := self.db.Begin() + for _, rpf := range rpfs { + // parse identifiers + tx.Where("tpid = ?", tpid). + Where("direction = ?", rpf.Direction). + Where("tenant = ?", rpf.Tenant). + Where("subject = ?", rpf.Subject). + Where("category = ?", rpf.Category). + Where("loadid = ?", rpf.LoadId). + Delete(TpRatingProfile{}) + for _, ra := range rpf.RatingPlanActivations { + tx.Save(TpRatingProfile{ + Tpid: rpf.TPid, + Loadid: rpf.LoadId, + Tenant: rpf.Tenant, + Category: rpf.Category, + Subject: rpf.Subject, + Direction: rpf.Direction, + ActivationTime: ra.ActivationTime, + RatingPlanId: ra.RatingPlanId, + FallbackSubjects: ra.FallbackSubjects, + }) } } - buffer.WriteString(" ON DUPLICATE KEY UPDATE fallback_subjects=values(fallback_subjects)") - if _, err := self.Db.Exec(buffer.String()); err != nil { - return err - } + tx.Commit() return nil } @@ -1238,44 +1245,50 @@ func (self *SQLStorage) GetTpRatingPlans(tpid, tag string) (map[string][]*utils. } func (self *SQLStorage) GetTpRatingProfiles(qryRpf *utils.TPRatingProfile) (map[string]*utils.TPRatingProfile, error) { - q := fmt.Sprintf("SELECT loadid,direction,tenant,category,subject,activation_time,rating_plan_id,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) - } + + rpfs := make(map[string]*utils.TPRatingProfile) + var tpRpfs []TpRatingProfile + q := self.db.Where("tpid = ?", qryRpf.TPid) if len(qryRpf.Direction) != 0 { - q += fmt.Sprintf(" AND direction='%s'", qryRpf.Direction) + q = q.Where("direction = ?", qryRpf.Direction) } if len(qryRpf.Tenant) != 0 { - q += fmt.Sprintf(" AND tenant='%s'", qryRpf.Tenant) + q = q.Where("tenant = ?", qryRpf.Tenant) } if len(qryRpf.Category) != 0 { - q += fmt.Sprintf(" AND category='%s'", qryRpf.Category) + q = q.Where("category = ?", qryRpf.Category) } - if len(qryRpf.Subject) != 0 { - q += fmt.Sprintf(" AND subject='%s'", qryRpf.Subject) + q = q.Where("subject = ?", qryRpf.Subject) } - rows, err := self.Db.Query(q) - if err != nil { + if len(qryRpf.LoadId) != 0 { + q = q.Where("loadid = ?", qryRpf.LoadId) + } + if err := q.Find(&tpRpfs).Error; err != nil { return nil, err } - defer rows.Close() - rpfs := make(map[string]*utils.TPRatingProfile) - for rows.Next() { - var rcvLoadId, tenant, category, direction, subject, fallback_subjects, rating_plan_tag, activation_time string - if err := rows.Scan(&rcvLoadId, &direction, &tenant, &category, &subject, &activation_time, &rating_plan_tag, &fallback_subjects); err != nil { - return nil, err + for _, tpRpf := range tpRpfs { + + rp := &utils.TPRatingProfile{ + TPid: tpRpf.Tpid, + LoadId: tpRpf.Loadid, + Direction: tpRpf.Direction, + Tenant: tpRpf.Tenant, + Category: tpRpf.Category, + Subject: tpRpf.Subject, } - rp := &utils.TPRatingProfile{TPid: qryRpf.TPid, LoadId: rcvLoadId, Direction: direction, Tenant: tenant, Category: category, 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}} + ra := &utils.TPRatingActivation{ + ActivationTime: tpRpf.ActivationTime, + RatingPlanId: tpRpf.RatingPlanId, + FallbackSubjects: tpRpf.FallbackSubjects, + } + if existingRpf, exists := rpfs[rp.KeyId()]; !exists { + rp.RatingPlanActivations = []*utils.TPRatingActivation{ra} rpfs[rp.KeyId()] = rp } else { // Exists, update - existingRp.RatingPlanActivations = append(existingRp.RatingPlanActivations, - &utils.TPRatingActivation{ActivationTime: activation_time, RatingPlanId: rating_plan_tag, FallbackSubjects: fallback_subjects}) + existingRpf.RatingPlanActivations = append(existingRpf.RatingPlanActivations, ra) } + } return rpfs, nil } diff --git a/utils/apitpdata.go b/utils/apitpdata.go index dca7cf53c..5064bc9d6 100644 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -170,11 +170,35 @@ func (self *TPRatingProfile) KeyId() string { return fmt.Sprintf("%s:%s:%s:%s", self.Direction, self.Tenant, self.Category, self.Subject) } +func (rpf *TPRatingProfile) GetRatingProfilesId() string { + return rpf.LoadId + + TP_ID_SEP + + rpf.Direction + + TP_ID_SEP + + rpf.Tenant + + TP_ID_SEP + + rpf.Category + + TP_ID_SEP + + rpf.Subject +} + +func (rpf *TPRatingProfile) SetRatingProfilesId(id string) error { + ids := strings.Split(id, TP_ID_SEP) + if len(ids) != 5 { + return fmt.Errorf("Wrong TP Rating Profiles Id: %s", id) + } + rpf.LoadId = ids[0] + rpf.Direction = ids[1] + rpf.Tenant = ids[2] + rpf.Category = ids[3] + rpf.Subject = ids[4] + return nil +} + type TPRatingActivation struct { ActivationTime string // Time when this profile will become active, defined as unix epoch time RatingPlanId string // Id of RatingPlan profile FallbackSubjects string // So we follow the api - Weight float64 } // Helper to return the subject fallback keys we need in dataDb