From 891f407b54e0b2f24fc5a0e76941d15fc25f6256 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 18 Aug 2015 19:19:56 +0300 Subject: [PATCH] added remove rating profile api + console --- apier/v1/apier.go | 49 ++++++++++++++++++++++++-- console/ratingprofile_rem.go | 66 ++++++++++++++++++++++++++++++++++++ engine/ratingprofile.go | 3 +- engine/storage_interface.go | 1 + engine/storage_map.go | 17 +++++++++- engine/storage_mongo.go | 2 +- engine/storage_redis.go | 22 +++++++++++- history/file_scribe.go | 2 +- history/mock_scribe.go | 2 +- history/scribe.go | 10 ++++-- history/scribe_test.go | 26 +++++++++----- 11 files changed, 180 insertions(+), 20 deletions(-) create mode 100644 console/ratingprofile_rem.go diff --git a/apier/v1/apier.go b/apier/v1/apier.go index 72ca292f4..26397e691 100644 --- a/apier/v1/apier.go +++ b/apier/v1/apier.go @@ -34,7 +34,7 @@ import ( ) const ( - OK = "OK" + OK = utils.OK ) type ApierV1 struct { @@ -925,7 +925,7 @@ func (self *ApierV1) ReloadCache(attrs utils.ApiReloadCache, reply *string) erro }); err != nil { return err } - *reply = "OK" + *reply = utils.OK return nil } @@ -1122,6 +1122,49 @@ func (self *ApierV1) LoadTariffPlanFromFolder(attrs utils.AttrLoadTpFromFolder, return err } } - *reply = "OK" + *reply = utils.OK + return nil +} + +type AttrRemoveRatingProfile struct { + Direction string + Tennat string + Category string + Subject string +} + +func (arrp *AttrRemoveRatingProfile) GetId() (result string) { + if arrp.Direction != "" && arrp.Direction != utils.ANY { + result += arrp.Direction + result += utils.CONCATENATED_KEY_SEP + } else { + return + } + if arrp.Tennat != "" && arrp.Tennat != utils.ANY { + result += arrp.Tennat + result += utils.CONCATENATED_KEY_SEP + } else { + return + } + + if arrp.Category != "" && arrp.Category != utils.ANY { + result += arrp.Category + result += utils.CONCATENATED_KEY_SEP + } else { + return + } + if arrp.Subject != "" && arrp.Subject != utils.ANY { + result += arrp.Subject + } + return +} + +func (self *ApierV1) RemoveRatingProfile(attr AttrRemoveRatingProfile, reply *string) error { + err := self.RatingDb.RemoveRatingProfile(attr.GetId()) + if err != nil { + *reply = err.Error() + return err + } + *reply = utils.OK return nil } diff --git a/console/ratingprofile_rem.go b/console/ratingprofile_rem.go new file mode 100644 index 000000000..600503038 --- /dev/null +++ b/console/ratingprofile_rem.go @@ -0,0 +1,66 @@ +/* +Rating system designed to be used in VoIP Carriers World +Copyright (C) 2012-2015 ITsysCOM + +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 console + +import ( + "github.com/cgrates/cgrates/apier/v1" + "github.com/cgrates/cgrates/utils" +) + +func init() { + c := &CmdRemRatingProfile{ + name: "ratingprofile_rem", + rpcMethod: "ApierV1.RemoveRatingProfile", + } + commands[c.Name()] = c + c.CommandExecuter = &CommandExecuter{c} +} + +// Commander implementation +type CmdRemRatingProfile struct { + name string + rpcMethod string + rpcParams *v1.AttrRemoveRatingProfile + rpcResult string + *CommandExecuter +} + +func (self *CmdRemRatingProfile) Name() string { + return self.name +} + +func (self *CmdRemRatingProfile) RpcMethod() string { + return self.rpcMethod +} + +func (self *CmdRemRatingProfile) RpcParams(reset bool) interface{} { + if reset || self.rpcParams == nil { + self.rpcParams = &v1.AttrRemoveRatingProfile{Direction: utils.OUT} + } + return self.rpcParams +} + +func (self *CmdRemRatingProfile) PostprocessRpcParams() error { + return nil +} + +func (self *CmdRemRatingProfile) RpcResult() interface{} { + var s string + return &s +} diff --git a/engine/ratingprofile.go b/engine/ratingprofile.go index 10737c5f6..21dc074e1 100644 --- a/engine/ratingprofile.go +++ b/engine/ratingprofile.go @@ -199,12 +199,13 @@ func (rp *RatingProfile) GetRatingPlansForPrefix(cd *CallDescriptor) (err error) } // history record method -func (rpf *RatingProfile) GetHistoryRecord() history.Record { +func (rpf *RatingProfile) GetHistoryRecord(deleted bool) history.Record { js, _ := json.Marshal(rpf) return history.Record{ Id: rpf.Id, Filename: history.RATING_PROFILES_FN, Payload: js, + Deleted: deleted, } } diff --git a/engine/storage_interface.go b/engine/storage_interface.go index c043271ef..3d8f263fa 100644 --- a/engine/storage_interface.go +++ b/engine/storage_interface.go @@ -46,6 +46,7 @@ type RatingStorage interface { SetRatingPlan(*RatingPlan) error GetRatingProfile(string, bool) (*RatingProfile, error) SetRatingProfile(*RatingProfile) error + RemoveRatingProfile(string) error GetDestination(string) (*Destination, error) SetDestination(*Destination) error GetLCR(string, bool) (*LCR, error) diff --git a/engine/storage_map.go b/engine/storage_map.go index 2045ad720..7e0e24229 100644 --- a/engine/storage_map.go +++ b/engine/storage_map.go @@ -312,7 +312,22 @@ func (ms *MapStorage) SetRatingProfile(rpf *RatingProfile) (err error) { ms.dict[utils.RATING_PROFILE_PREFIX+rpf.Id] = result response := 0 if historyScribe != nil { - go historyScribe.Record(rpf.GetHistoryRecord(), &response) + go historyScribe.Record(rpf.GetHistoryRecord(false), &response) + } + return +} + +func (ms *MapStorage) RemoveRatingProfile(key string) (err error) { + for k := range ms.dict { + if strings.HasPrefix(k, key) { + delete(ms.dict, key) + cache2go.RemKey(k) + response := 0 + rpf := &RatingProfile{Id: key} + if historyScribe != nil { + go historyScribe.Record(rpf.GetHistoryRecord(true), &response) + } + } } return } diff --git a/engine/storage_mongo.go b/engine/storage_mongo.go index 050fa3d16..b18ca86bf 100644 --- a/engine/storage_mongo.go +++ b/engine/storage_mongo.go @@ -156,7 +156,7 @@ func (ms *MongoStorage) GetRatingProfile(key string) (rp *RatingProfile, err err func (ms *MongoStorage) SetRatingProfile(rp *RatingProfile) error { if historyScribe != nil { response := 0 - historyScribe.Record(rp.GetHistoryRecord(), &response) + historyScribe.Record(rp.GetHistoryRecord(false), &response) } return ms.db.C("ratingprofiles").Insert(rp) } diff --git a/engine/storage_redis.go b/engine/storage_redis.go index c65da0bd2..527b5150a 100644 --- a/engine/storage_redis.go +++ b/engine/storage_redis.go @@ -399,11 +399,31 @@ func (rs *RedisStorage) SetRatingProfile(rpf *RatingProfile) (err error) { err = rs.db.Set(utils.RATING_PROFILE_PREFIX+rpf.Id, result) if err == nil && historyScribe != nil { response := 0 - go historyScribe.Record(rpf.GetHistoryRecord(), &response) + go historyScribe.Record(rpf.GetHistoryRecord(false), &response) } return } +func (rs *RedisStorage) RemoveRatingProfile(key string) error { + keys, err := rs.db.Keys(utils.RATING_PROFILE_PREFIX + key + "*") + if err != nil { + return err + } + for _, key := range keys { + _, err = rs.db.Del(key) + if err != nil { + return err + } + cache2go.RemKey(key) + rpf := &RatingProfile{Id: key} + if err == nil && historyScribe != nil { + response := 0 + go historyScribe.Record(rpf.GetHistoryRecord(true), &response) + } + } + return nil +} + func (rs *RedisStorage) GetLCR(key string, skipCache bool) (lcr *LCR, err error) { key = utils.LCR_PREFIX + key if !skipCache { diff --git a/history/file_scribe.go b/history/file_scribe.go index f7d0c94d1..8d023ba09 100644 --- a/history/file_scribe.go +++ b/history/file_scribe.go @@ -63,7 +63,7 @@ func NewFileScribe(fileRoot string, saveInterval time.Duration) (*FileScribe, er func (s *FileScribe) Record(rec Record, out *int) error { s.mu.Lock() fileToSave := rec.Filename - recordsMap[fileToSave] = recordsMap[fileToSave].SetOrAdd(&rec) + recordsMap[fileToSave] = recordsMap[fileToSave].Modify(&rec) // flood protection for save method (do not save on every loop iteration) if s.waitingFile == fileToSave { diff --git a/history/mock_scribe.go b/history/mock_scribe.go index 25e292ff2..1192f9cb4 100644 --- a/history/mock_scribe.go +++ b/history/mock_scribe.go @@ -40,7 +40,7 @@ func NewMockScribe() (*MockScribe, error) { func (s *MockScribe) Record(rec Record, out *int) error { s.mu.Lock() fn := rec.Filename - recordsMap[fn] = recordsMap[fn].SetOrAdd(&rec) + recordsMap[fn] = recordsMap[fn].Modify(&rec) s.mu.Unlock() s.save(fn) return nil diff --git a/history/scribe.go b/history/scribe.go index a96195772..819bada44 100644 --- a/history/scribe.go +++ b/history/scribe.go @@ -38,6 +38,7 @@ type Record struct { Id string Filename string Payload []byte + Deleted bool } type records []*Record @@ -63,12 +64,17 @@ func (rs records) Sort() { sort.Sort(rs) } -func (rs records) SetOrAdd(rec *Record) records { +func (rs records) Modify(rec *Record) records { //rs.Sort() n := len(rs) i := sort.Search(n, func(i int) bool { return rs[i].Id >= rec.Id }) if i < n && rs[i].Id == rec.Id { - rs[i] = rec + if rec.Deleted { + // delete + rs = append(rs[:i], rs[i+1:]...) + } else { + rs[i] = rec + } } else { // i is the index where it would be inserted. rs = append(rs, nil) diff --git a/history/scribe_test.go b/history/scribe_test.go index 47e17985e..41828d482 100644 --- a/history/scribe_test.go +++ b/history/scribe_test.go @@ -26,7 +26,7 @@ import ( func TestHistorySet(t *testing.T) { rs := records{&Record{Id: "first"}} second := &Record{Id: "first"} - rs.SetOrAdd(second) + rs.Modify(second) if len(rs) != 1 || rs[0] != second { t.Error("error setting new value: ", rs[0]) } @@ -35,18 +35,26 @@ func TestHistorySet(t *testing.T) { func TestHistoryAdd(t *testing.T) { rs := records{&Record{Id: "first"}} second := &Record{Id: "second"} - rs = rs.SetOrAdd(second) + rs = rs.Modify(second) if len(rs) != 2 || rs[1] != second { t.Error("error setting new value: ", rs) } } -func BenchmarkSetOrAdd(b *testing.B) { - var rs records - for i := 0; i < 1000; i++ { - rs = rs.SetOrAdd(&Record{Id: strconv.Itoa(i)}) - } - for i := 0; i < b.N; i++ { - rs.SetOrAdd(&Record{Id: "400"}) +func TestHistoryRemove(t *testing.T) { + rs := records{&Record{Id: "first"}, &Record{Id: "second"}} + rs = rs.Modify(&Record{Id: "first", Deleted: true}) + if len(rs) != 1 || rs[0].Id != "second" { + t.Error("error deleting record: ", rs) + } +} + +func BenchmarkModify(b *testing.B) { + var rs records + for i := 0; i < 1000; i++ { + rs = rs.Modify(&Record{Id: strconv.Itoa(i)}) + } + for i := 0; i < b.N; i++ { + rs.Modify(&Record{Id: "400"}) } }