diff --git a/timespans/activationperiod_test.go b/timespans/activationperiod_test.go index 961afdb9e..c057b521c 100644 --- a/timespans/activationperiod_test.go +++ b/timespans/activationperiod_test.go @@ -64,7 +64,7 @@ func TestApRestoreFromStorage(t *testing.T) { Tenant: "CUSTOMER_1", Subject: "rif:from:tm", Destination: "49"} - cd.SearchStorageForPrefix() + cd.LoadActivationPeriods() if len(cd.ActivationPeriods) != 2 { t.Error("Error restoring activation periods: ", len(cd.ActivationPeriods)) } @@ -110,7 +110,7 @@ func TestApStoreRestoreBlank(t *testing.T) { func TestFallbackDirect(t *testing.T) { cd := &CallDescriptor{TOR: "0", Direction: "OUT", Tenant: "CUSTOMER_2", Subject: "danb:87.139.12.167", Destination: "41"} - cd.SearchStorageForPrefix() + cd.LoadActivationPeriods() if len(cd.ActivationPeriods) != 1 { t.Error("Error restoring activation periods: ", len(cd.ActivationPeriods)) } @@ -118,7 +118,7 @@ func TestFallbackDirect(t *testing.T) { func TestFallbackWithBackTrace(t *testing.T) { cd := &CallDescriptor{TOR: "0", Direction: "OUT", Tenant: "CUSTOMER_2", Subject: "danb:87.139.12.167", Destination: "4123"} - cd.SearchStorageForPrefix() + cd.LoadActivationPeriods() if len(cd.ActivationPeriods) != 1 { t.Error("Error restoring activation periods: ", len(cd.ActivationPeriods)) } @@ -126,7 +126,7 @@ func TestFallbackWithBackTrace(t *testing.T) { func TestFallbackDefault(t *testing.T) { cd := &CallDescriptor{TOR: "0", Direction: "OUT", Tenant: "CUSTOMER_2", Subject: "danb:87.139.12.167", Destination: "4123"} - cd.SearchStorageForPrefix() + cd.LoadActivationPeriods() if len(cd.ActivationPeriods) != 1 { t.Error("Error restoring activation periods: ", len(cd.ActivationPeriods)) } @@ -134,7 +134,7 @@ func TestFallbackDefault(t *testing.T) { func TestFallbackNoInfiniteLoop(t *testing.T) { cd := &CallDescriptor{Tenant: "vdf", Subject: "rif", Destination: "0721"} - cd.SearchStorageForPrefix() + cd.LoadActivationPeriods() if len(cd.ActivationPeriods) != 0 { t.Error("Error restoring activation periods: ", len(cd.ActivationPeriods)) } diff --git a/timespans/calldesc.go b/timespans/calldesc.go index 44e669623..acd3d9b19 100644 --- a/timespans/calldesc.go +++ b/timespans/calldesc.go @@ -36,11 +36,8 @@ func init() { } const ( - // the minimum length for a destination prefix to be matched. - MinPrefixLength = 2 - RecursionMaxDepth = 4 - FallbackDestination = "fallback" // the string to be used to mark the fallback destination - FallbackSubject = "*all" + RECURSION_MAX_DEPTH = 4 + FALLBACK_SUBJECT = "*all" ) var ( @@ -80,7 +77,6 @@ type CallDescriptor struct { Amount float64 FallbackSubject string // the subject to check for destination if not found on primary subject ActivationPeriods []*ActivationPeriod - FallbackKey string userBalance *UserBalance } @@ -89,22 +85,6 @@ func (cd *CallDescriptor) AddActivationPeriod(aps ...*ActivationPeriod) { cd.ActivationPeriods = append(cd.ActivationPeriods, aps...) } -// Adds an activation period that applyes to current call descriptor if not already present. -func (cd *CallDescriptor) AddActivationPeriodIfNotPresent(aps ...*ActivationPeriod) { - for _, ap := range aps { - found := false - for _, eap := range cd.ActivationPeriods { - if ap.Equal(eap) { - found = true - break - } - } - if !found { - cd.ActivationPeriods = append(cd.ActivationPeriods, ap) - } - } -} - // Returns the key used to retrive the user balance involved in this call func (cd *CallDescriptor) GetUserBalanceKey() string { subj := cd.Subject @@ -148,32 +128,17 @@ func SetDebitPeriod(d time.Duration) { /* Restores the activation periods for the specified prefix from storage. */ -func (cd *CallDescriptor) SearchStorageForPrefix() (destPrefix string, err error) { +func (cd *CallDescriptor) LoadActivationPeriods() (destPrefix string, err error) { if val, err := cache.GetXCached(cd.GetKey()); err == nil { xaps := val.(xCachedActivationPeriods) cd.ActivationPeriods = xaps.aps return xaps.destPrefix, nil } - cd.ActivationPeriods = make([]*ActivationPeriod, 0) - base := fmt.Sprintf("%s:%s:%s:%s:", cd.Direction, cd.Tenant, cd.TOR, cd.Subject) - destPrefix = cd.Destination - key := base + destPrefix - values, err := cd.getActivationPeriodsOrFallback(key, base, destPrefix, 1) - if err != nil { - // use the default destination - key := base + FallbackDestination - values, err = cd.getActivationPeriodsOrFallback(key, base, destPrefix, 1) - } + destPrefix, values, err := cd.getActivationPeriodsForPrefix(cd.GetKey(), 1) if err != nil { + fallbackKey := fmt.Sprintf("%s:%s:%s:%s", cd.Direction, cd.Tenant, cd.TOR, FALLBACK_SUBJECT) // use the default subject - base = fmt.Sprintf("%s:%s:%s:%s:", cd.Direction, cd.Tenant, cd.TOR, FallbackSubject) - key = base + destPrefix - values, err = cd.getActivationPeriodsOrFallback(key, base, destPrefix, 1) - if err != nil { - // use the default destination - key := base + FallbackDestination - values, err = cd.getActivationPeriodsOrFallback(key, base, destPrefix, 1) - } + destPrefix, values, err = cd.getActivationPeriodsForPrefix(fallbackKey, 1) } //load the activation preriods if err == nil && len(values) > 0 { @@ -184,37 +149,23 @@ func (cd *CallDescriptor) SearchStorageForPrefix() (destPrefix string, err error return } -func (cd *CallDescriptor) getActivationPeriodsOrFallback(key, base, destPrefix string, recursionDepth int) (values []*ActivationPeriod, err error) { - if recursionDepth > RecursionMaxDepth { +func (cd *CallDescriptor) getActivationPeriodsForPrefix(key string, recursionDepth int) (foundPrefix string, aps []*ActivationPeriod, err error) { + if recursionDepth > RECURSION_MAX_DEPTH { err = errors.New("Max fallback recursion depth reached!" + key) return } - values, fallbackKey, err := storageGetter.GetActivationPeriodsOrFallback(key) - if fallbackKey != "" { - base = fallbackKey + ":" - key = base + destPrefix - recursionDepth++ - return cd.getActivationPeriodsOrFallback(key, base, destPrefix, recursionDepth) + rp, err := storageGetter.GetRatingProfile(key) + if err != nil { + return "", nil, err } - - //get for a smaller prefix if the orignal one was not found - for i := len(cd.Destination); err != nil || fallbackKey != ""; { - if fallbackKey != "" { - base = fallbackKey + ":" - key = base + destPrefix + foundPrefix, aps, err = rp.GetActivationPeriodsForPrefix(cd.Destination) + if err != nil { + if rp.FallbackKey != "" { recursionDepth++ - return cd.getActivationPeriodsOrFallback(key, base, destPrefix, recursionDepth) + return cd.getActivationPeriodsForPrefix(rp.FallbackKey, recursionDepth) } - - i-- - if i >= MinPrefixLength { - destPrefix = cd.Destination[:i] - key = base + destPrefix - } else { - break - } - values, fallbackKey, err = storageGetter.GetActivationPeriodsOrFallback(key) } + return } @@ -223,7 +174,7 @@ Constructs the key for the storage lookup. The prefixLen is limiting the length of the destination prefix. */ func (cd *CallDescriptor) GetKey() string { - return fmt.Sprintf("%s:%s:%s:%s:%s", cd.Direction, cd.Tenant, cd.TOR, cd.Subject, cd.Destination) + return fmt.Sprintf("%s:%s:%s:%s", cd.Direction, cd.Tenant, cd.TOR, cd.Subject) } /* @@ -306,7 +257,7 @@ func (cd *CallDescriptor) splitTimeSpan(firstSpan *TimeSpan) (timespans []*TimeS Creates a CallCost structure with the cost information calculated for the received CallDescriptor. */ func (cd *CallDescriptor) GetCost() (*CallCost, error) { - destPrefix, err := cd.SearchStorageForPrefix() + destPrefix, err := cd.LoadActivationPeriods() timespans := cd.splitInTimeSpans() cost := 0.0 connectionFee := 0.0 @@ -336,7 +287,7 @@ and will decrease it by 10% for nine times. So if the user has little credit it If the user has no credit then it will return 0. */ func (cd *CallDescriptor) GetMaxSessionTime() (seconds float64, err error) { - _, err = cd.SearchStorageForPrefix() + _, err = cd.LoadActivationPeriods() now := time.Now() availableCredit, availableSeconds := 0.0, 0.0 if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil { diff --git a/timespans/calldesc_test.go b/timespans/calldesc_test.go index 81d15a1c2..6619cc002 100644 --- a/timespans/calldesc_test.go +++ b/timespans/calldesc_test.go @@ -57,7 +57,7 @@ func TestSplitSpans(t *testing.T) { t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC) cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2} - cd.SearchStorageForPrefix() + cd.LoadActivationPeriods() timespans := cd.splitInTimeSpans() if len(timespans) != 2 { t.Log(cd.ActivationPeriods) @@ -70,7 +70,7 @@ func TestRedisSplitSpans(t *testing.T) { t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC) cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0257", TimeStart: t1, TimeEnd: t2} - cd.SearchStorageForPrefix() + cd.LoadActivationPeriods() timespans := cd.splitInTimeSpans() if len(timespans) != 2 { t.Log(cd.ActivationPeriods) @@ -215,22 +215,6 @@ func TestMaxSessionTimeNoCredit(t *testing.T) { } } -func TestApAddAPIfNotPresent(t *testing.T) { - ap1 := &ActivationPeriod{ActivationTime: time.Date(2012, time.July, 2, 14, 24, 30, 0, time.UTC)} - ap2 := &ActivationPeriod{ActivationTime: time.Date(2012, time.July, 2, 14, 24, 30, 0, time.UTC)} - ap3 := &ActivationPeriod{ActivationTime: time.Date(2012, time.July, 2, 14, 24, 30, 1, time.UTC)} - cd := &CallDescriptor{} - cd.AddActivationPeriodIfNotPresent(ap1) - cd.AddActivationPeriodIfNotPresent(ap2) - if len(cd.ActivationPeriods) != 1 { - t.Error("Wronfully appended activation period ;)", len(cd.ActivationPeriods)) - } - cd.AddActivationPeriodIfNotPresent(ap3) - if len(cd.ActivationPeriods) != 2 { - t.Error("Wronfully not appended activation period ;)", len(cd.ActivationPeriods)) - } -} - /*********************************** BENCHMARKS ***************************************/ func BenchmarkStorageGetting(b *testing.B) { b.StopTimer() @@ -239,7 +223,7 @@ func BenchmarkStorageGetting(b *testing.B) { cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2} b.StartTimer() for i := 0; i < b.N; i++ { - storageGetter.GetActivationPeriodsOrFallback(cd.GetKey()) + storageGetter.GetRatingProfile(cd.GetKey()) } } @@ -250,7 +234,7 @@ func BenchmarkStorageRestoring(b *testing.B) { cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2} b.StartTimer() for i := 0; i < b.N; i++ { - cd.SearchStorageForPrefix() + cd.LoadActivationPeriods() } } @@ -270,7 +254,7 @@ func BenchmarkSplitting(b *testing.B) { t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC) t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC) cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2} - cd.SearchStorageForPrefix() + cd.LoadActivationPeriods() b.StartTimer() for i := 0; i < b.N; i++ { cd.splitInTimeSpans() diff --git a/timespans/csvreader.go b/timespans/csvreader.go index 407c6109c..5a20b8f01 100644 --- a/timespans/csvreader.go +++ b/timespans/csvreader.go @@ -39,7 +39,7 @@ type CSVReader struct { rates map[string][]*Rate timings map[string][]*Timing activationPeriods map[string]*ActivationPeriod - ratingProfiles map[string]CallDescriptors + ratingProfiles map[string]*RatingProfile } func NewFileCSVReader() *CSVReader { @@ -50,7 +50,7 @@ func NewFileCSVReader() *CSVReader { c.rates = make(map[string][]*Rate) c.timings = make(map[string][]*Timing) c.activationPeriods = make(map[string]*ActivationPeriod) - c.ratingProfiles = make(map[string]CallDescriptors) + c.ratingProfiles = make(map[string]*RatingProfile) c.readerFunc = openFileCSVReader return c } @@ -98,22 +98,20 @@ func (csvr *CSVReader) WriteToDatabase(storage StorageGetter, flush, verbose boo if verbose { log.Print("Rating profiles") } - for _, cds := range csvr.ratingProfiles { - for _, cd := range cds { - err = storage.SetActivationPeriodsOrFallback(cd.GetKey(), cd.ActivationPeriods, cd.FallbackKey) - if err != nil { - return err - } - if verbose { - log.Print(cd.GetKey()) - } + for _, rp := range csvr.ratingProfiles { + err = storage.SetRatingProfile(rp) + if err != nil { + return err + } + if verbose { + log.Print(rp.Id) } } if verbose { log.Print("Action timings") } for k, ats := range csvr.actionsTimings { - err = storage.SetActionTimings(ACTION_TIMING_PREFIX+":"+k, ats) + err = storage.SetActionTimings(ACTION_TIMING_PREFIX+k, ats) if err != nil { return err } @@ -283,46 +281,24 @@ func (csvr *CSVReader) LoadRatingProfiles(fn string, comma rune) (err error) { if err != nil { return errors.New(fmt.Sprintf("Cannot parse activation time from %v", record[6])) } + key := fmt.Sprintf("%s:%s:%s:%s", direction, tenant, tor, subject) + rp, ok := csvr.ratingProfiles[key] + if !ok { + rp = &RatingProfile{Id: key} + csvr.ratingProfiles[key] = rp + } for _, d := range csvr.destinations { - for _, p := range d.Prefixes { //destinations - // Search for a CallDescriptor with the same key - var cd *CallDescriptor - key := fmt.Sprintf("%s:%s:%s:%s:%s", direction, tenant, tor, subject, p) - for _, c := range csvr.ratingProfiles[p] { - if c.GetKey() == key { - cd = c - } - } - if cd == nil { - cd = &CallDescriptor{ - Direction: direction, - Tenant: tenant, - TOR: tor, - Subject: subject, - Destination: p, - } - csvr.ratingProfiles[p] = append(csvr.ratingProfiles[p], cd) - } + for _, p := range d.Prefixes { //destinations ap, exists := csvr.activationPeriods[record[5]] if !exists { return errors.New(fmt.Sprintf("Could not load ratinTiming for tag: ", record[5])) } - newAP := &ActivationPeriod{} + newAP := &ActivationPeriod{ActivationTime: at} //copy(newAP.Intervals, ap.Intervals) newAP.Intervals = append(newAP.Intervals, ap.Intervals...) - newAP.ActivationTime = at - cd.AddActivationPeriodIfNotPresent(newAP) - if fallbacksubject != "" && - csvr.ratingProfiles[p].getKey(fmt.Sprintf("%s:%s:%s:%s:%s", direction, tenant, tor, subject, FallbackDestination)) == nil { - cd = &CallDescriptor{ - Direction: direction, - Tenant: tenant, - TOR: tor, - Subject: subject, - Destination: FallbackDestination, - FallbackKey: fmt.Sprintf("%s:%s:%s:%s", direction, tenant, tor, fallbacksubject), - } - csvr.ratingProfiles[p] = append(csvr.ratingProfiles[p], cd) + rp.AddActivationPeriodIfNotPresent(p, newAP) + if fallbacksubject != "" { + rp.FallbackKey = fmt.Sprintf("%s:%s:%s:%s", direction, tenant, tor, fallbacksubject) } } } diff --git a/timespans/csvreader_helpers.go b/timespans/csvreader_helpers.go index 6b30ffe6d..443b7511c 100644 --- a/timespans/csvreader_helpers.go +++ b/timespans/csvreader_helpers.go @@ -118,17 +118,6 @@ func (rt *RateTiming) GetInterval(r *Rate) (i *Interval) { return } -type CallDescriptors []*CallDescriptor - -func (cds CallDescriptors) getKey(key string) *CallDescriptor { - for _, cd := range cds { - if cd.GetKey() == key { - return cd - } - } - return nil -} - func ValidateCSVData(fn string, re *regexp.Regexp) (err error) { fin, err := os.Open(fn) if err != nil { diff --git a/timespans/csvreader_test.go b/timespans/csvreader_test.go index efc341639..b1a5da15a 100644 --- a/timespans/csvreader_test.go +++ b/timespans/csvreader_test.go @@ -129,7 +129,7 @@ func TestLoadRateTimings(t *testing.T) { } func TestLoadRatingProfiles(t *testing.T) { - if len(csvr.ratingProfiles) != 7 { + if len(csvr.ratingProfiles) != 6 { t.Error("Failed to load rating profiles: ", len(csvr.ratingProfiles), csvr.ratingProfiles) } } diff --git a/timespans/destinations.go b/timespans/destinations.go index 8d5a4c9dd..e011101a0 100644 --- a/timespans/destinations.go +++ b/timespans/destinations.go @@ -52,7 +52,7 @@ func (d *Destination) containsPrefix(prefix string) (bool, int) { if d == nil { return false, 0 } - for i := len(prefix); i >= MinPrefixLength; { + for i := len(prefix); i >= MIN_PREFIX_LENGTH; { for _, p := range d.Prefixes { if p == prefix[:i] { return true, i diff --git a/timespans/ratingprofile.go b/timespans/ratingprofile.go new file mode 100644 index 000000000..029eee3a0 --- /dev/null +++ b/timespans/ratingprofile.go @@ -0,0 +1,105 @@ +/* +Rating system designed to be used in VoIP Carriers World +Copyright (C) 2012 Radu Ioan Fericean + +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 timespans + +import ( + "errors" + "strings" +) + +const ( + // the minimum length for a destination prefix to be matched. + MIN_PREFIX_LENGTH = 2 +) + +type RatingProfile struct { + Id string `bson:"_id,omitempty"` + FallbackKey string + DestinationMap map[string][]*ActivationPeriod +} + +func (rp *RatingProfile) store() (result string) { + result += rp.FallbackKey + ">" + for k, aps := range rp.DestinationMap { + result += k + "=" + for _, ap := range aps { + result += ap.store() + "<" + } + result = strings.TrimRight(result, "<") + result += ">" + } + result = strings.TrimRight(result, ">") + return +} + +func (rp *RatingProfile) restore(input string) { + if rp.DestinationMap == nil { + rp.DestinationMap = make(map[string][]*ActivationPeriod, 1) + } + elements := strings.Split(input, ">") + rp.FallbackKey = elements[0] + for _, kv := range elements[1:] { + pair := strings.SplitN(kv, "=", 2) + apList := strings.Split(pair[1], "<") + var newAps []*ActivationPeriod + for _, aps := range apList { + ap := new(ActivationPeriod) + ap.restore(aps) + newAps = append(newAps, ap) + } + rp.DestinationMap[pair[0]] = newAps + } +} + +// Adds an activation period that applyes to current rating profile if not already present. +func (rp *RatingProfile) AddActivationPeriodIfNotPresent(destInfo string, aps ...*ActivationPeriod) { + if rp.DestinationMap == nil { + rp.DestinationMap = make(map[string][]*ActivationPeriod, 1) + } + for _, ap := range aps { + found := false + for _, eap := range rp.DestinationMap[destInfo] { + if ap.Equal(eap) { + found = true + break + } + } + if !found { + rp.DestinationMap[destInfo] = append(rp.DestinationMap[destInfo], ap) + } + } +} + +func (rp *RatingProfile) GetActivationPeriodsForPrefix(destPrefix string) (foundPrefix string, aps []*ActivationPeriod, err error) { + var found = false + foundPrefix = destPrefix + for i := len(foundPrefix); !found; { + if i >= MIN_PREFIX_LENGTH { + foundPrefix = foundPrefix[:i] + } else { + break + } + aps, found = rp.DestinationMap[foundPrefix] + i-- + } + if !found { + return "", nil, errors.New("not found") + } + return +} diff --git a/timespans/ratingprofile_test.go b/timespans/ratingprofile_test.go new file mode 100644 index 000000000..228173d8a --- /dev/null +++ b/timespans/ratingprofile_test.go @@ -0,0 +1,64 @@ +/* +Rating system designed to be used in VoIP Carriers World +Copyright (C) 2012 Radu Ioan Fericean + +This program is free software: you can Storagetribute 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 timespans + +import ( + "reflect" + "testing" + "time" +) + +func TestRpStoreRestore(t *testing.T) { + d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC) + i := &Interval{ + Months: Months{time.February}, + MonthDays: MonthDays{1}, + WeekDays: []time.Weekday{time.Wednesday, time.Thursday}, + StartTime: "14:30:00", + EndTime: "15:00:00"} + ap := &ActivationPeriod{ActivationTime: d} + ap.AddInterval(i) + rp := &RatingProfile{FallbackKey: "test"} + rp.AddActivationPeriodIfNotPresent("0723", ap) + result := rp.store() + expected := "test>0723=1328106601000000000|;2;1;3,4;14:30:00;15:00:00;0;0;0;0;0" + if result != expected { + t.Errorf("Expected %q was %q", expected, result) + } + ap1 := ActivationPeriod{} + ap1.restore(result) + if reflect.DeepEqual(ap, ap1) { + t.Errorf("Expected %v was %v", ap, ap1) + } +} +func TestRpAddAPIfNotPresent(t *testing.T) { + ap1 := &ActivationPeriod{ActivationTime: time.Date(2012, time.July, 2, 14, 24, 30, 0, time.UTC)} + ap2 := &ActivationPeriod{ActivationTime: time.Date(2012, time.July, 2, 14, 24, 30, 0, time.UTC)} + ap3 := &ActivationPeriod{ActivationTime: time.Date(2012, time.July, 2, 14, 24, 30, 1, time.UTC)} + rp := &RatingProfile{} + rp.AddActivationPeriodIfNotPresent("test", ap1) + rp.AddActivationPeriodIfNotPresent("test", ap2) + if len(rp.DestinationMap["test"]) != 1 { + t.Error("Wronfully appended activation period ;)", len(rp.DestinationMap["test"])) + } + rp.AddActivationPeriodIfNotPresent("test", ap3) + if len(rp.DestinationMap["test"]) != 2 { + t.Error("Wronfully not appended activation period ;)", len(rp.DestinationMap["test"])) + } +} diff --git a/timespans/storage_interface.go b/timespans/storage_interface.go index aff065790..cb6e1727b 100644 --- a/timespans/storage_interface.go +++ b/timespans/storage_interface.go @@ -22,7 +22,6 @@ import ( "bytes" "encoding/gob" "encoding/json" - "errors" "strings" ) @@ -38,8 +37,8 @@ Interface for storage providers. type StorageGetter interface { Close() Flush() error - GetActivationPeriodsOrFallback(string) ([]*ActivationPeriod, string, error) - SetActivationPeriodsOrFallback(string, []*ActivationPeriod, string) error + GetRatingProfile(string) (*RatingProfile, error) + SetRatingProfile(*RatingProfile) error GetDestination(string) (*Destination, error) SetDestination(*Destination) error GetActions(string) ([]*Action, error) @@ -117,22 +116,16 @@ type MyMarshaler struct { func (mm *MyMarshaler) Marshal(v interface{}) (data []byte, err error) { switch v.(type) { - case []*ActivationPeriod: - result := "" - for _, ap := range v.([]*ActivationPeriod) { - result += ap.store() + "\n" - } - return []byte(result), nil case []*Action: result := "" for _, a := range v.([]*Action) { - result += a.store() + "\n" + result += a.store() + "+" } return []byte(result), nil case []*ActionTiming: result := "" for _, at := range v.([]*ActionTiming) { - result += at.store() + "\n" + result += at.store() + "+" } return []byte(result), nil case storer: @@ -148,23 +141,9 @@ func (mm *MyMarshaler) Marshal(v interface{}) (data []byte, err error) { func (mm *MyMarshaler) Unmarshal(data []byte, v interface{}) (err error) { switch v.(type) { - case *[]*ActivationPeriod: - aps := v.(*[]*ActivationPeriod) - splits := strings.Split(string(data), "\n") - if len(splits) == 0 { - return errors.New("") - } - for _, ap_string := range splits { - if len(ap_string) > 0 { - ap := &ActivationPeriod{} - ap.restore(ap_string) - *aps = append(*aps, ap) - } - } - return nil case *[]*Action: as := v.(*[]*Action) - for _, a_string := range strings.Split(string(data), "\n") { + for _, a_string := range strings.Split(string(data), "+") { if len(a_string) > 0 { a := &Action{} a.restore(a_string) @@ -174,7 +153,7 @@ func (mm *MyMarshaler) Unmarshal(data []byte, v interface{}) (err error) { return nil case *[]*ActionTiming: ats := v.(*[]*ActionTiming) - for _, at_string := range strings.Split(string(data), "\n") { + for _, at_string := range strings.Split(string(data), "+") { if len(at_string) > 0 { at := &ActionTiming{} at.restore(at_string) diff --git a/timespans/storage_map.go b/timespans/storage_map.go index a54cff6da..3ca820ef4 100644 --- a/timespans/storage_map.go +++ b/timespans/storage_map.go @@ -41,27 +41,19 @@ func (ms *MapStorage) Flush() error { return nil } -func (ms *MapStorage) GetActivationPeriodsOrFallback(key string) (aps []*ActivationPeriod, fallbackKey string, err error) { - elem, ok := ms.dict[key] - if !ok { - err = errors.New(fmt.Sprintf("%s not found!", key)) - return - } - err = ms.ms.Unmarshal(elem, &aps) - if err != nil { - err = ms.ms.Unmarshal(elem, &fallbackKey) +func (ms *MapStorage) GetRatingProfile(key string) (rp *RatingProfile, err error) { + if values, ok := ms.dict[key]; ok { + rp = new(RatingProfile) + err = ms.ms.Unmarshal(values, rp) + } else { + return nil, errors.New("not found") } return } -func (ms *MapStorage) SetActivationPeriodsOrFallback(key string, aps []*ActivationPeriod, fallbackKey string) (err error) { - var result []byte - if len(aps) > 0 { - result, err = ms.ms.Marshal(aps) - } else { - result, err = ms.ms.Marshal(fallbackKey) - } - ms.dict[key] = result +func (ms *MapStorage) SetRatingProfile(rp *RatingProfile) (err error) { + result, err := ms.ms.Marshal(rp) + ms.dict[rp.Id] = result return } diff --git a/timespans/storage_mongo.go b/timespans/storage_mongo.go index 5fbfdbe94..1d1c15f36 100644 --- a/timespans/storage_mongo.go +++ b/timespans/storage_mongo.go @@ -46,12 +46,11 @@ func NewMongoStorage(host, port, db, user, pass string) (StorageGetter, error) { ndb := session.DB(db) session.SetMode(mgo.Monotonic, true) index := mgo.Index{Key: []string{"key"}, Background: true} - err = ndb.C("activationperiods").EnsureIndex(index) - err = ndb.C("destinations").EnsureIndex(index) err = ndb.C("actions").EnsureIndex(index) - index = mgo.Index{Key: []string{"id"}, Background: true} - err = ndb.C("userbalances").EnsureIndex(index) err = ndb.C("actiontimings").EnsureIndex(index) + index = mgo.Index{Key: []string{"id"}, Background: true} + err = ndb.C("destinations").EnsureIndex(index) + err = ndb.C("userbalances").EnsureIndex(index) return &MongoStorage{db: ndb, session: session}, nil } @@ -61,7 +60,7 @@ func (ms *MongoStorage) Close() { } func (ms *MongoStorage) Flush() (err error) { - err = ms.db.C("activationperiods").DropCollection() + err = ms.db.C("ratingprofiles").DropCollection() if err != nil { return } @@ -84,15 +83,6 @@ func (ms *MongoStorage) Flush() (err error) { return nil } -/* -Helper type for activation periods storage. -*/ -type ApKeyValue struct { - Key string - FallbackKey string `omitempty` - Value []*ActivationPeriod -} - type AcKeyValue struct { Key string Value []*Action @@ -121,14 +111,14 @@ type LogTriggerEntry struct { LogTime time.Time } -func (ms *MongoStorage) GetActivationPeriodsOrFallback(key string) ([]*ActivationPeriod, string, error) { - result := new(ApKeyValue) - err := ms.db.C("activationperiods").Find(bson.M{"key": key}).One(&result) - return result.Value, result.FallbackKey, err +func (ms *MongoStorage) GetRatingProfile(key string) (rp *RatingProfile, err error) { + rp = new(RatingProfile) + err = ms.db.C("ratingprofiles").Find(bson.M{"_id": key}).One(&rp) + return } -func (ms *MongoStorage) SetActivationPeriodsOrFallback(key string, aps []*ActivationPeriod, fallbackKey string) error { - return ms.db.C("activationperiods").Insert(&ApKeyValue{Key: key, FallbackKey: fallbackKey, Value: aps}) +func (ms *MongoStorage) SetRatingProfile(rp *RatingProfile) error { + return ms.db.C("ratingprofiles").Insert(rp) } func (ms *MongoStorage) GetDestination(key string) (result *Destination, err error) { diff --git a/timespans/storage_postgres.go b/timespans/storage_postgres.go index 5b20160b9..d4e98e122 100644 --- a/timespans/storage_postgres.go +++ b/timespans/storage_postgres.go @@ -43,11 +43,11 @@ func (psl *PostgresStorage) Flush() (err error) { return } -func (psl *PostgresStorage) GetActivationPeriodsOrFallback(string) (aps []*ActivationPeriod, fallback string, err error) { +func (psl *PostgresStorage) GetRatingProfile(string) (rp *RatingProfile, err error) { return } -func (psl *PostgresStorage) SetActivationPeriodsOrFallback(key string, aps []*ActivationPeriod, fallback string) (err error) { +func (psl *PostgresStorage) SetRatingProfile(rp *RatingProfile) (err error) { return } diff --git a/timespans/storage_redis.go b/timespans/storage_redis.go index c65d7856b..d1337ea65 100644 --- a/timespans/storage_redis.go +++ b/timespans/storage_redis.go @@ -47,26 +47,19 @@ func (rs *RedisStorage) Flush() error { return rs.db.Flushdb() } -func (rs *RedisStorage) GetActivationPeriodsOrFallback(key string) (aps []*ActivationPeriod, fallbackKey string, err error) { - elem, err := rs.db.Get(key) - if err != nil { - return - } - err = rs.ms.Unmarshal(elem, &aps) - if err != nil { - err = rs.ms.Unmarshal(elem, &fallbackKey) +func (rs *RedisStorage) GetRatingProfile(key string) (rp *RatingProfile, err error) { + if values, err := rs.db.Get(key); err == nil { + rp = new(RatingProfile) + err = rs.ms.Unmarshal(values, rp) + } else { + return nil, err } return } -func (rs *RedisStorage) SetActivationPeriodsOrFallback(key string, aps []*ActivationPeriod, fallbackKey string) (err error) { - var result []byte - if len(aps) > 0 { - result, err = rs.ms.Marshal(aps) - } else { - result, err = rs.ms.Marshal(fallbackKey) - } - return rs.db.Set(key, result) +func (rs *RedisStorage) SetRatingProfile(rp *RatingProfile) (err error) { + result, err := rs.ms.Marshal(rp) + return rs.db.Set(rp.Id, result) } func (rs *RedisStorage) GetDestination(key string) (dest *Destination, err error) { @@ -78,6 +71,7 @@ func (rs *RedisStorage) GetDestination(key string) (dest *Destination, err error } return } + func (rs *RedisStorage) SetDestination(dest *Destination) (err error) { result, err := rs.ms.Marshal(dest) return rs.db.Set(dest.Id, result)