diff --git a/rater/action.go b/rater/action.go index e0af15d3d..2cc8e66db 100644 --- a/rater/action.go +++ b/rater/action.go @@ -21,8 +21,6 @@ package rater import ( "fmt" "sort" - "strconv" - "strings" ) /* @@ -38,6 +36,8 @@ type Action struct { MinuteBucket *MinuteBucket } +type Actions []*Action + type actionTypeFunc func(*UserBalance, *Action) error func getActionFunc(typ string) (actionTypeFunc, bool) { @@ -194,40 +194,3 @@ func (apl ActionPriotityList) Less(i, j int) bool { func (apl ActionPriotityList) Sort() { sort.Sort(apl) } - -/* -Serializes the action for the storage. Used for key-value storages. -*/ -func (a *Action) store() (result string) { - result += a.Id + "|" - result += a.ActionType + "|" - result += a.BalanceId + "|" - result += a.Direction + "|" - result += strconv.FormatFloat(a.Units, 'f', -1, 64) + "|" - result += strconv.FormatFloat(a.Weight, 'f', -1, 64) - if a.MinuteBucket != nil { - result += "|" - result += a.MinuteBucket.store() - } - return -} - -/* -De-serializes the action for the storage. Used for key-value storages. -*/ -func (a *Action) restore(input string) { - elements := strings.Split(input, "|") - if len(elements) < 6 { - return - } - a.Id = elements[0] - a.ActionType = elements[1] - a.BalanceId = elements[2] - a.Direction = elements[3] - a.Units, _ = strconv.ParseFloat(elements[4], 64) - a.Weight, _ = strconv.ParseFloat(elements[5], 64) - if len(elements) == 7 { - a.MinuteBucket = &MinuteBucket{} - a.MinuteBucket.restore(elements[6]) - } -} diff --git a/rater/action_timing.go b/rater/action_timing.go index 22913b25f..13e69d094 100644 --- a/rater/action_timing.go +++ b/rater/action_timing.go @@ -45,6 +45,8 @@ type ActionTiming struct { stCache time.Time } +type ActionTimings []*ActionTiming + func (at *ActionTiming) GetNextStartTime() (t time.Time) { if !at.stCache.IsZero() { return at.stCache @@ -277,45 +279,6 @@ func (at *ActionTiming) String() string { return at.Tag + " " + at.GetNextStartTime().String() + ",w: " + strconv.FormatFloat(at.Weight, 'f', -1, 64) } -/* -Serializes the action timing for the storage. Used for key-value storages. -*/ -func (at *ActionTiming) store() (result string) { - result += at.Id + "|" - result += at.Tag + "|" - for _, ubi := range at.UserBalanceIds { - result += ubi + "," - } - result = strings.TrimRight(result, ",") + "|" - if at.Timing != nil { - result += at.Timing.store() + "|" - } else { - result += " |" - } - result += strconv.FormatFloat(at.Weight, 'f', -1, 64) + "|" - result += at.ActionsId - return -} - -/* -De-serializes the action timing for the storage. Used for key-value storages. -*/ -func (at *ActionTiming) restore(input string) { - elements := strings.Split(input, "|") - at.Id = elements[0] - at.Tag = elements[1] - for _, ubi := range strings.Split(elements[2], ",") { - if strings.TrimSpace(ubi) != "" { - at.UserBalanceIds = append(at.UserBalanceIds, ubi) - } - } - - at.Timing = &Interval{} - at.Timing.restore(elements[3]) - at.Weight, _ = strconv.ParseFloat(elements[4], 64) - at.ActionsId = elements[5] -} - // helper function for uuid generation func GenUUID() string { uuid := make([]byte, 16) diff --git a/rater/action_trigger.go b/rater/action_trigger.go index 1728005cb..9a98df131 100644 --- a/rater/action_trigger.go +++ b/rater/action_trigger.go @@ -22,8 +22,6 @@ import ( "fmt" //"log" "sort" - "strconv" - "strings" ) type ActionTrigger struct { @@ -79,36 +77,3 @@ func (atpl ActionTriggerPriotityList) Less(i, j int) bool { func (atpl ActionTriggerPriotityList) Sort() { sort.Sort(atpl) } - -/* -Serializes the action trigger for the storage. Used for key-value storages. -*/ -func (at *ActionTrigger) store() (result string) { - result += at.Id + ";" - result += at.BalanceId + ";" - result += at.Direction + ";" - result += at.DestinationId + ";" - result += at.ActionsId + ";" - result += strconv.FormatFloat(at.ThresholdValue, 'f', -1, 64) + ";" - result += strconv.FormatFloat(at.Weight, 'f', -1, 64) + ";" - result += strconv.FormatBool(at.Executed) - return -} - -/* -De-serializes the action timing for the storage. Used for key-value storages. -*/ -func (at *ActionTrigger) restore(input string) { - elements := strings.Split(input, ";") - if len(elements) != 8 { - return - } - at.Id = elements[0] - at.BalanceId = elements[1] - at.Direction = elements[2] - at.DestinationId = elements[3] - at.ActionsId = elements[4] - at.ThresholdValue, _ = strconv.ParseFloat(elements[5], 64) - at.Weight, _ = strconv.ParseFloat(elements[6], 64) - at.Executed, _ = strconv.ParseBool(elements[7]) -} diff --git a/rater/actions_test.go b/rater/actions_test.go index b1e651683..62078c8b6 100644 --- a/rater/actions_test.go +++ b/rater/actions_test.go @@ -19,64 +19,10 @@ along with this program. If not, see package rater import ( - "reflect" - //"strings" "testing" "time" ) -func TestActionTimingStoreRestore(t *testing.T) { - i := &Interval{ - Months: Months{time.January, time.February, time.March, time.April, time.May, time.June, time.July, time.August, time.September, time.October, time.November, time.December}, - MonthDays: MonthDays{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, - WeekDays: WeekDays{time.Monday, time.Tuesday, time.Wednesday, time.Thursday, time.Friday}, - StartTime: "18:00:00", - EndTime: "00:00:00", - Weight: 10.0, - ConnectFee: 0.0, - Price: 1.0, - PricedUnits: 60, - RateIncrements: 1, - } - at := &ActionTiming{ - Id: "some uuid", - Tag: "test", - UserBalanceIds: []string{"one", "two", "three"}, - Timing: i, - Weight: 10.0, - ActionsId: "Commando", - } - r := at.store() - if string(r) != "some uuid|test|one,two,three|;1,2,3,4,5,6,7,8,9,10,11,12;1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31;1,2,3,4,5;18:00:00;00:00:00;10;0;1;60;1|10|Commando" { - t.Errorf("Error serializing action timing: %v", string(r)) - } - o := &ActionTiming{} - o.restore(r) - if !reflect.DeepEqual(o, at) { - t.Errorf("Expected %v was %v", at, o) - } -} -func TestActionTriggerStoreRestore(t *testing.T) { - at := &ActionTrigger{ - Id: "some_uuid", - BalanceId: CREDIT, - Direction: OUTBOUND, - ThresholdValue: 100.0, - DestinationId: "NAT", - Weight: 10.0, - ActionsId: "Commando", - } - r := at.store() - if string(r) != "some_uuid;MONETARY;OUT;NAT;Commando;100;10;false" { - t.Errorf("Error serializing action trigger: %v", string(r)) - } - o := &ActionTrigger{} - o.restore(r) - if !reflect.DeepEqual(o, at) { - t.Errorf("Expected %v was %v", at, o) - } -} - func TestActionTimingNothing(t *testing.T) { at := &ActionTiming{} st := at.GetNextStartTime() diff --git a/rater/activationperiod_test.go b/rater/activationperiod_test.go index 8e1c216e2..c3a5866c9 100644 --- a/rater/activationperiod_test.go +++ b/rater/activationperiod_test.go @@ -161,7 +161,7 @@ func BenchmarkActivationPeriodStoreRestoreJson(b *testing.B) { func BenchmarkActivationPeriodRestore(b *testing.B) { ap := &ActivationPeriod{} for i := 0; i < b.N; i++ { - activationPeriodRestore("1328106601000000000|;2;1;3,4;14:30:00;15:00:00;0;0;0;0;0", ap) + ap.Restore("1328106601000000000|;2;1;3,4;14:30:00;15:00:00;0;0;0;0;0") } } @@ -176,11 +176,11 @@ func BenchmarkActivationPeriodStoreRestore(b *testing.B) { ap := &ActivationPeriod{ActivationTime: d} ap.AddInterval(i) - ap1 := ActivationPeriod{} + ap1 := &ActivationPeriod{} b.StartTimer() for i := 0; i < b.N; i++ { - result, _ := Marshal(ap) - Unmarshal(result, ap1) + result, _ := ap.Store() + ap1.Restore(result) } } @@ -195,7 +195,7 @@ func BenchmarkActivationPeriodMarshallerMyStoreRestore(b *testing.B) { ap := &ActivationPeriod{ActivationTime: d} ap.AddInterval(i) - ap1 := ActivationPeriod{} + ap1 := &ActivationPeriod{} b.StartTimer() ms := new(MyMarshaler) for i := 0; i < b.N; i++ { diff --git a/rater/dateseries.go b/rater/dateseries.go index 167eb5b97..31d3f9538 100644 --- a/rater/dateseries.go +++ b/rater/dateseries.go @@ -73,23 +73,6 @@ func (ys *Years) Parse(input, sep string) { } } -func (yss Years) store() (result string) { - for _, ys := range yss { - result += strconv.Itoa(int(ys)) + "," - } - result = strings.TrimRight(result, ",") - return -} - -func (yss *Years) restore(input string) { - for _, ys := range strings.Split(input, ",") { - if ys != "" { - mm, _ := strconv.Atoi(ys) - *yss = append(*yss, mm) - } - } -} - // Defines months series type Months []time.Month @@ -138,23 +121,6 @@ func (m *Months) Parse(input, sep string) { } } -func (ms Months) store() (result string) { - for _, m := range ms { - result += strconv.Itoa(int(m)) + "," - } - result = strings.TrimRight(result, ",") - return -} - -func (ms *Months) restore(input string) { - for _, m := range strings.Split(input, ",") { - if m != "" { - mm, _ := strconv.Atoi(m) - *ms = append(*ms, time.Month(mm)) - } - } -} - // Defines month days series type MonthDays []int @@ -203,23 +169,6 @@ func (md *MonthDays) Parse(input, sep string) { } } -func (mds MonthDays) store() (result string) { - for _, md := range mds { - result += strconv.Itoa(int(md)) + "," - } - result = strings.TrimRight(result, ",") - return -} - -func (mds *MonthDays) restore(input string) { - for _, md := range strings.Split(input, ",") { - if md != "" { - mm, _ := strconv.Atoi(md) - *mds = append(*mds, mm) - } - } -} - // Defines week days series type WeekDays []time.Weekday @@ -266,20 +215,3 @@ func (wd *WeekDays) Parse(input, sep string) { } } } - -func (wds WeekDays) store() (result string) { - for _, wd := range wds { - result += strconv.Itoa(int(wd)) + "," - } - result = strings.TrimRight(result, ",") - return -} - -func (wds *WeekDays) restore(input string) { - for _, wd := range strings.Split(input, ",") { - if wd != "" { - mm, _ := strconv.Atoi(wd) - *wds = append(*wds, time.Weekday(mm)) - } - } -} diff --git a/rater/dateseries_test.go b/rater/dateseries_test.go index 830ef6043..401a5604e 100644 --- a/rater/dateseries_test.go +++ b/rater/dateseries_test.go @@ -25,58 +25,6 @@ import ( "time" ) -func TestMonthYearStoreRestore(t *testing.T) { - y := Years{2010, 2011, 2012} - r := y.store() - if string(r) != "2010,2011,2012" { - t.Errorf("Error serializing years: %v", string(r)) - } - o := Years{} - o.restore(r) - if !reflect.DeepEqual(o, y) { - t.Errorf("Expected %v was %v", y, o) - } -} - -func TestMonthStoreRestore(t *testing.T) { - m := Months{5, 6, 7, 8} - r := m.store() - if string(r) != "5,6,7,8" { - t.Errorf("Error serializing months: %v", string(r)) - } - o := Months{} - o.restore(r) - if !reflect.DeepEqual(o, m) { - t.Errorf("Expected %v was %v", m, o) - } -} - -func TestMonthDayStoreRestore(t *testing.T) { - md := MonthDays{24, 25, 26} - r := md.store() - if string(r) != "24,25,26" { - t.Errorf("Error serializing month days: %v", string(r)) - } - o := MonthDays{} - o.restore(r) - if !reflect.DeepEqual(o, md) { - t.Errorf("Expected %v was %v", md, o) - } -} - -func TestWeekDayStoreRestore(t *testing.T) { - wd := WeekDays{time.Saturday, time.Sunday} - r := wd.store() - if string(r) != "6,0" { - t.Errorf("Error serializing week days: %v", string(r)) - } - o := WeekDays{} - o.restore(r) - if !reflect.DeepEqual(o, wd) { - t.Errorf("Expected %v was %v", wd, o) - } -} - func TestMonthStoreRestoreJson(t *testing.T) { m := Months{5, 6, 7, 8} r, _ := json.Marshal(m) diff --git a/rater/destinations.go b/rater/destinations.go index a841be14a..aa61a69f5 100644 --- a/rater/destinations.go +++ b/rater/destinations.go @@ -69,15 +69,3 @@ func (d *Destination) String() (result string) { result = strings.TrimRight(result, ", ") return result } - -func (d *Destination) store() (result string) { - for _, p := range d.Prefixes { - result += p + "," - } - result = strings.TrimRight(result, ",") - return -} - -func (d *Destination) restore(input string) { - d.Prefixes = strings.Split(input, ",") -} diff --git a/rater/interval.go b/rater/interval.go index f72aaae47..05f2aa0ad 100644 --- a/rater/interval.go +++ b/rater/interval.go @@ -132,42 +132,3 @@ func (i *Interval) Equal(o *Interval) bool { i.StartTime == o.StartTime && i.EndTime == o.EndTime } - -/* -Serializes the intervals for the storag. Used for key-value storages. -*/ -func (i *Interval) store() (result string) { - result += i.Years.store() + ";" - result += i.Months.store() + ";" - result += i.MonthDays.store() + ";" - result += i.WeekDays.store() + ";" - result += i.StartTime + ";" - result += i.EndTime + ";" - result += strconv.FormatFloat(i.Weight, 'f', -1, 64) + ";" - result += strconv.FormatFloat(i.ConnectFee, 'f', -1, 64) + ";" - result += strconv.FormatFloat(i.Price, 'f', -1, 64) + ";" - result += strconv.FormatFloat(i.PricedUnits, 'f', -1, 64) + ";" - result += strconv.FormatFloat(i.RateIncrements, 'f', -1, 64) - return -} - -/* -De-serializes the interval for the storage. Used for key-value storages. -*/ -func (i *Interval) restore(input string) { - is := strings.Split(input, ";") - if len(is) != 11 { - return - } - i.Years.restore(is[0]) - i.Months.restore(is[1]) - i.MonthDays.restore(is[2]) - i.WeekDays.restore(is[3]) - i.StartTime = is[4] - i.EndTime = is[5] - i.Weight, _ = strconv.ParseFloat(is[6], 64) - i.ConnectFee, _ = strconv.ParseFloat(is[7], 64) - i.Price, _ = strconv.ParseFloat(is[8], 64) - i.PricedUnits, _ = strconv.ParseFloat(is[9], 64) - i.RateIncrements, _ = strconv.ParseFloat(is[10], 64) -} diff --git a/rater/interval_test.go b/rater/interval_test.go index adb8efa90..58c71b193 100644 --- a/rater/interval_test.go +++ b/rater/interval_test.go @@ -19,44 +19,10 @@ along with this program. If not, see package rater import ( - "reflect" "testing" "time" ) -func TestIntervalStoreRestore(t *testing.T) { - i := &Interval{ - Months: Months{time.January, time.February, time.March, time.April, time.May, time.June, time.July, time.August, time.September, time.October, time.November, time.December}, - MonthDays: MonthDays{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, - WeekDays: WeekDays{time.Monday, time.Tuesday, time.Wednesday, time.Thursday, time.Friday}, - StartTime: "18:00:00", - EndTime: "00:00:00", - Weight: 10.0, - ConnectFee: 0.0, - Price: 1.0, - PricedUnits: 60, - RateIncrements: 1, - } - r := i.store() - if string(r) != ";1,2,3,4,5,6,7,8,9,10,11,12;1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31;1,2,3,4,5;18:00:00;00:00:00;10;0;1;60;1" { - t.Errorf("Error serializing interval: %v", string(r)) - } - o := &Interval{} - o.restore(r) - if !reflect.DeepEqual(o, i) { - t.Errorf("Expected %v was %v", i, o) - } -} - -func TestIntervalRestoreFromString(t *testing.T) { - s := ";1,2,3,4,5,6,7,8,9,10,11,12;1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31;1,2,3,4,5,6,0;00:00:00;;10;0;0.2;60;1" - i := Interval{} - i.restore(s) - if i.Price != 0.2 { - t.Errorf("Error restoring inteval period from string %+v", i) - } -} - func TestIntervalMonth(t *testing.T) { i := &Interval{Months: Months{time.February}} d := time.Date(2012, time.February, 10, 23, 0, 0, 0, time.UTC) diff --git a/rater/minute_buckets.go b/rater/minute_buckets.go index 760739c9f..61bae4bcb 100644 --- a/rater/minute_buckets.go +++ b/rater/minute_buckets.go @@ -21,8 +21,6 @@ package rater import ( "math" "sort" - "strconv" - "strings" ) type MinuteBucket struct { @@ -84,29 +82,3 @@ func (bs bucketsorter) Less(j, i int) bool { func (bs bucketsorter) Sort() { sort.Sort(bs) } - -/* -Serializes the minute bucket for the storage. Used for key-value storages. -*/ -func (mb *MinuteBucket) store() (result string) { - result += strconv.FormatFloat(mb.Seconds, 'f', -1, 64) + ";" - result += strconv.FormatFloat(mb.Weight, 'f', -1, 64) + ";" - result += strconv.FormatFloat(mb.Price, 'f', -1, 64) + ";" - result += strconv.FormatFloat(mb.Percent, 'f', -1, 64) + ";" - result += mb.DestinationId - return -} - -/* -De-serializes the minute bucket for the storage. Used for key-value storages. -*/ -func (mb *MinuteBucket) restore(input string) { - elements := strings.Split(input, ";") - if len(elements) == 5 { - mb.Seconds, _ = strconv.ParseFloat(elements[0], 64) - mb.Weight, _ = strconv.ParseFloat(elements[1], 64) - mb.Price, _ = strconv.ParseFloat(elements[2], 64) - mb.Percent, _ = strconv.ParseFloat(elements[3], 64) - mb.DestinationId = elements[4] - } -} diff --git a/rater/simple_marshaller.go b/rater/simple_marshaller.go deleted file mode 100644 index 604d88e99..000000000 --- a/rater/simple_marshaller.go +++ /dev/null @@ -1,114 +0,0 @@ -/* -Rating system designed to be used in VoIP Carriers World -Copyright (C) 2013 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 rater - -import ( - "errors" - "strconv" - "strings" - "time" -) - -func Marshal(v interface{}) ([]byte, error) { - switch i := v.(type) { - case *ActivationPeriod: - result, err := activationPeriodStore(i) - return []byte(result), err - case *RatingProfile: - result, err := ratingProfileStore(i) - return []byte(result), err - } - return nil, errors.New("Not supported type") -} - -func Unmarshal(data []byte, v interface{}) error { - switch i := v.(type) { - case *ActivationPeriod: - return activationPeriodRestore(string(data), i) - case *RatingProfile: - return ratingProfileRestore(string(data), i) - } - return errors.New("Not supported type") -} - -func activationPeriodStore(ap *ActivationPeriod) (result string, err error) { - result += strconv.FormatInt(ap.ActivationTime.UnixNano(), 10) + "|" - for _, i := range ap.Intervals { - result += i.store() + "|" - } - result = strings.TrimRight(result, "|") - return -} - -func activationPeriodRestore(input string, ap *ActivationPeriod) error { - elements := strings.Split(input, "|") - unixNano, _ := strconv.ParseInt(elements[0], 10, 64) - ap.ActivationTime = time.Unix(0, unixNano).In(time.UTC) - els := elements[1:] - if len(els) > 1 { - els = elements[1 : len(elements)-1] - } - for _, is := range els { - i := &Interval{} - i.restore(is) - ap.Intervals = append(ap.Intervals, i) - } - return nil -} - -func ratingProfileStore(rp *RatingProfile) (result string, err error) { - result += rp.FallbackKey + ">" - for k, aps := range rp.DestinationMap { - result += k + "=" - for _, ap := range aps { - aps, err := activationPeriodStore(ap) - if err != nil { - return result, err - } - result += aps + "<" - } - result = strings.TrimRight(result, "<") - result += ">" - } - result = strings.TrimRight(result, ">") - return -} - -func ratingProfileRestore(input string, rp *RatingProfile) error { - 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) - err := activationPeriodRestore(aps, ap) - if err != nil { - return err - } - newAps = append(newAps, ap) - } - rp.DestinationMap[pair[0]] = newAps - } - return nil -} diff --git a/rater/simple_marshaller_test.go b/rater/simple_marshaller_test.go deleted file mode 100644 index 129528afe..000000000 --- a/rater/simple_marshaller_test.go +++ /dev/null @@ -1,80 +0,0 @@ -/* -Rating system designed to be used in VoIP Carriers World -Copyright (C) 2013 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 rater - -import ( - "reflect" - "testing" - "time" -) - -func TestSimpleMarshallerApStoreRestore(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) - result, err := Marshal(ap) - expected := []byte("1328106601000000000|;2;1;3,4;14:30:00;15:00:00;0;0;0;0;0") - if err != nil || !reflect.DeepEqual(result, expected) { - t.Errorf("Expected %q was %q", expected, result) - } - ap1 := &ActivationPeriod{} - err = Unmarshal(result, ap1) - if err != nil || !reflect.DeepEqual(ap, ap1) { - t.Errorf("Expected %v was %v: %v", ap, ap1, err) - } -} - -func TestSimpleMarshallerApRestoreFromString(t *testing.T) { - s := []byte("1325376000000000000|;1,2,3,4,5,6,7,8,9,10,11,12;1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31;1,2,3,4,5,6,0;00:00:00;;10;0;0.2;60;1\n") - ap := &ActivationPeriod{} - err := Unmarshal(s, ap) - if err != nil || len(ap.Intervals) != 1 { - t.Error("Error restoring activation period from string", ap) - } -} - -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, err := Marshal(rp) - expected := []byte("test>0723=1328106601000000000|;2;1;3,4;14:30:00;15:00:00;0;0;0;0;0") - if err != nil || !reflect.DeepEqual(result, expected) { - t.Errorf("Expected %q was %q", expected, result) - } - rp1 := &RatingProfile{} - err = Unmarshal(result, rp1) - if err != nil || !reflect.DeepEqual(rp, rp1) { - t.Errorf("Expected %v was %v", rp, rp1) - } -} diff --git a/rater/simple_serializer.go b/rater/simple_serializer.go new file mode 100644 index 000000000..c1139bfc8 --- /dev/null +++ b/rater/simple_serializer.go @@ -0,0 +1,550 @@ +/* +Rating system designed to be used in VoIP Carriers World +Copyright (C) 2013 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 rater + +import ( + "errors" + "strconv" + "strings" + "time" +) + +type Serializer interface { + Store() (string, error) + Restore(string) error +} + +var notEnoughElements = errors.New("Too few elements to restore") + +func (ap *ActivationPeriod) Store() (result string, err error) { + result += strconv.FormatInt(ap.ActivationTime.UnixNano(), 10) + "|" + for _, i := range ap.Intervals { + str, err := i.Store() + if err != nil { + return "", err + } + result += str + "|" + } + result = strings.TrimRight(result, "|") + return +} + +func (ap *ActivationPeriod) Restore(input string) error { + elements := strings.Split(input, "|") + unixNano, _ := strconv.ParseInt(elements[0], 10, 64) + ap.ActivationTime = time.Unix(0, unixNano).In(time.UTC) + els := elements[1:] + if len(els) > 1 { + els = elements[1 : len(elements)-1] + } + for _, is := range els { + i := &Interval{} + if err := i.Restore(is); err != nil { + return err + } + ap.Intervals = append(ap.Intervals, i) + } + return nil +} + +func (rp *RatingProfile) Store() (result string, err error) { + result += rp.FallbackKey + ">" + for k, aps := range rp.DestinationMap { + result += k + "=" + for _, ap := range aps { + aps, err := ap.Store() + if err != nil { + return result, err + } + result += aps + "<" + } + result = strings.TrimRight(result, "<") + result += ">" + } + result = strings.TrimRight(result, ">") + return +} + +func (rp *RatingProfile) Restore(input string) error { + 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) + err := ap.Restore(aps) + if err != nil { + return err + } + newAps = append(newAps, ap) + } + rp.DestinationMap[pair[0]] = newAps + } + return nil +} + +func (a *Action) Store() (result string, err error) { + result += a.Id + "|" + result += a.ActionType + "|" + result += a.BalanceId + "|" + result += a.Direction + "|" + result += strconv.FormatFloat(a.Units, 'f', -1, 64) + "|" + result += strconv.FormatFloat(a.Weight, 'f', -1, 64) + if a.MinuteBucket != nil { + result += "|" + str, err := a.MinuteBucket.Store() + if err != nil { + return "", err + } + result += str + } + return +} + +func (a *Action) Restore(input string) error { + elements := strings.Split(input, "|") + if len(elements) < 6 { + return notEnoughElements + } + a.Id = elements[0] + a.ActionType = elements[1] + a.BalanceId = elements[2] + a.Direction = elements[3] + a.Units, _ = strconv.ParseFloat(elements[4], 64) + a.Weight, _ = strconv.ParseFloat(elements[5], 64) + if len(elements) == 7 { + a.MinuteBucket = &MinuteBucket{} + if err := a.MinuteBucket.Restore(elements[6]); err != nil { + return err + } + } + return nil +} + +func (as Actions) Store() (result string, err error) { + for _, a := range as { + str, err := a.Store() + if err != nil { + return "", err + } + result += str + "~" + } + result = strings.TrimRight(result, "~") + return +} + +func (as *Actions) Restore(input string) error { + for _, a_string := range strings.Split(input, "~") { + if len(a_string) > 0 { + a := &Action{} + if err := a.Restore(a_string); err != nil { + return err + } + *as = append(*as, a) + } + } + return nil +} + +func (at *ActionTiming) Store() (result string, err error) { + result += at.Id + "|" + result += at.Tag + "|" + for _, ubi := range at.UserBalanceIds { + result += ubi + "," + } + result = strings.TrimRight(result, ",") + "|" + if at.Timing != nil { + str, err := at.Timing.Store() + if err != nil { + return "", err + } + result += str + "|" + } else { + result += " |" + } + result += strconv.FormatFloat(at.Weight, 'f', -1, 64) + "|" + result += at.ActionsId + return +} + +func (at *ActionTiming) Restore(input string) error { + elements := strings.Split(input, "|") + at.Id = elements[0] + at.Tag = elements[1] + for _, ubi := range strings.Split(elements[2], ",") { + if strings.TrimSpace(ubi) != "" { + at.UserBalanceIds = append(at.UserBalanceIds, ubi) + } + } + + at.Timing = &Interval{} + if err := at.Timing.Restore(elements[3]); err != nil { + return err + } + at.Weight, _ = strconv.ParseFloat(elements[4], 64) + at.ActionsId = elements[5] + return nil +} + +func (ats ActionTimings) Store() (result string, err error) { + for _, at := range ats { + str, err := at.Store() + if err != nil { + return "", err + } + result += str + "~" + } + result = strings.TrimRight(result, "~") + return +} + +func (ats *ActionTimings) Restore(input string) error { + for _, at_string := range strings.Split(input, "~") { + if len(at_string) > 0 { + at := &ActionTiming{} + if err := at.Restore(at_string); err != nil { + return err + } + *ats = append(*ats, at) + } + } + return nil +} + +func (at *ActionTrigger) Store() (result string, err error) { + result += at.Id + ";" + result += at.BalanceId + ";" + result += at.Direction + ";" + result += at.DestinationId + ";" + result += at.ActionsId + ";" + result += strconv.FormatFloat(at.ThresholdValue, 'f', -1, 64) + ";" + result += strconv.FormatFloat(at.Weight, 'f', -1, 64) + ";" + result += strconv.FormatBool(at.Executed) + return +} + +func (at *ActionTrigger) Restore(input string) error { + elements := strings.Split(input, ";") + if len(elements) != 8 { + return notEnoughElements + } + at.Id = elements[0] + at.BalanceId = elements[1] + at.Direction = elements[2] + at.DestinationId = elements[3] + at.ActionsId = elements[4] + at.ThresholdValue, _ = strconv.ParseFloat(elements[5], 64) + at.Weight, _ = strconv.ParseFloat(elements[6], 64) + at.Executed, _ = strconv.ParseBool(elements[7]) + return nil +} + +func (ub *UserBalance) Store() (result string, err error) { + result += ub.Id + "|" + result += ub.Type + "|" + for k, v := range ub.BalanceMap { + result += k + ":" + strconv.FormatFloat(v, 'f', -1, 64) + "#" + } + result = strings.TrimRight(result, "#") + "|" + for _, mb := range ub.MinuteBuckets { + str, err := mb.Store() + if err != nil { + return "", err + } + result += str + "#" + } + result = strings.TrimRight(result, "#") + "|" + for _, uc := range ub.UnitCounters { + str, err := uc.Store() + if err != nil { + return "", err + } + result += str + "#" + } + result = strings.TrimRight(result, "#") + "|" + for _, at := range ub.ActionTriggers { + res, err := at.Store() + if err != nil { + return "", err + } + result += res + "#" + } + result = strings.TrimRight(result, "#") + return +} + +func (ub *UserBalance) Restore(input string) error { + elements := strings.Split(input, "|") + if len(elements) < 2 { + return notEnoughElements + } + ub.Id = elements[0] + ub.Type = elements[1] + if ub.BalanceMap == nil { + ub.BalanceMap = make(map[string]float64, 0) + } + for _, maps := range strings.Split(elements[2], "#") { + kv := strings.Split(maps, ":") + if len(kv) != 2 { + continue + } + value, _ := strconv.ParseFloat(kv[1], 64) + ub.BalanceMap[kv[0]] = value + } + for _, mbs := range strings.Split(elements[3], "#") { + if mbs == "" { + continue + } + mb := &MinuteBucket{} + if err := mb.Restore(mbs); err != nil { + return err + } + ub.MinuteBuckets = append(ub.MinuteBuckets, mb) + } + for _, ucs := range strings.Split(elements[4], "#") { + if ucs == "" { + continue + } + uc := &UnitsCounter{} + if err := uc.Restore(ucs); err != nil { + return err + } + ub.UnitCounters = append(ub.UnitCounters, uc) + } + for _, ats := range strings.Split(elements[5], "#") { + if ats == "" { + continue + } + at := &ActionTrigger{} + if err := at.Restore(ats); err != nil { + return err + } + ub.ActionTriggers = append(ub.ActionTriggers, at) + } + return nil +} + +/* +Serializes the unit counter for the storage. Used for key-value storages. +*/ +func (uc *UnitsCounter) Store() (result string, err error) { + result += uc.Direction + "/" + result += uc.BalanceId + "/" + result += strconv.FormatFloat(uc.Units, 'f', -1, 64) + "/" + for _, mb := range uc.MinuteBuckets { + str, err := mb.Store() + if err != nil { + return "", err + } + result += str + "," + } + result = strings.TrimRight(result, ",") + return +} + +/* +De-serializes the unit counter for the storage. Used for key-value storages. +*/ +func (uc *UnitsCounter) Restore(input string) error { + elements := strings.Split(input, "/") + if len(elements) != 4 { + return notEnoughElements + } + uc.Direction = elements[0] + uc.BalanceId = elements[1] + uc.Units, _ = strconv.ParseFloat(elements[2], 64) + for _, mbs := range strings.Split(elements[3], ",") { + mb := &MinuteBucket{} + if err := mb.Restore(mbs); err != nil { + return err + } + uc.MinuteBuckets = append(uc.MinuteBuckets, mb) + } + return nil +} + +func (d *Destination) Store() (result string, err error) { + for _, p := range d.Prefixes { + result += p + "," + } + result = strings.TrimRight(result, ",") + return +} + +func (d *Destination) Restore(input string) error { + d.Prefixes = strings.Split(input, ",") + return nil +} + +func (i *Interval) Store() (result string, err error) { + str, err := i.Years.Store() + if err != nil { + return "", err + } + result += str + ";" + str, err = i.Months.Store() + if err != nil { + return "", err + } + result += str + ";" + str, err = i.MonthDays.Store() + if err != nil { + return "", err + } + result += str + ";" + str, err = i.WeekDays.Store() + if err != nil { + return "", err + } + result += str + ";" + result += i.StartTime + ";" + result += i.EndTime + ";" + result += strconv.FormatFloat(i.Weight, 'f', -1, 64) + ";" + result += strconv.FormatFloat(i.ConnectFee, 'f', -1, 64) + ";" + result += strconv.FormatFloat(i.Price, 'f', -1, 64) + ";" + result += strconv.FormatFloat(i.PricedUnits, 'f', -1, 64) + ";" + result += strconv.FormatFloat(i.RateIncrements, 'f', -1, 64) + return +} + +func (i *Interval) Restore(input string) error { + is := strings.Split(input, ";") + if len(is) != 11 { + return notEnoughElements + } + if err := i.Years.Restore(is[0]); err != nil { + return err + } + if err := i.Months.Restore(is[1]); err != nil { + return err + } + if err := i.MonthDays.Restore(is[2]); err != nil { + return err + } + if err := i.WeekDays.Restore(is[3]); err != nil { + return err + } + i.StartTime = is[4] + i.EndTime = is[5] + i.Weight, _ = strconv.ParseFloat(is[6], 64) + i.ConnectFee, _ = strconv.ParseFloat(is[7], 64) + i.Price, _ = strconv.ParseFloat(is[8], 64) + i.PricedUnits, _ = strconv.ParseFloat(is[9], 64) + i.RateIncrements, _ = strconv.ParseFloat(is[10], 64) + return nil +} + +func (mb *MinuteBucket) Store() (result string, err error) { + result += strconv.FormatFloat(mb.Seconds, 'f', -1, 64) + ";" + result += strconv.FormatFloat(mb.Weight, 'f', -1, 64) + ";" + result += strconv.FormatFloat(mb.Price, 'f', -1, 64) + ";" + result += strconv.FormatFloat(mb.Percent, 'f', -1, 64) + ";" + result += mb.DestinationId + return +} + +func (mb *MinuteBucket) Restore(input string) error { + elements := strings.Split(input, ";") + if len(elements) == 5 { + mb.Seconds, _ = strconv.ParseFloat(elements[0], 64) + mb.Weight, _ = strconv.ParseFloat(elements[1], 64) + mb.Price, _ = strconv.ParseFloat(elements[2], 64) + mb.Percent, _ = strconv.ParseFloat(elements[3], 64) + mb.DestinationId = elements[4] + return nil + } + return notEnoughElements +} + +func (wds WeekDays) Store() (result string, err error) { + for _, wd := range wds { + result += strconv.Itoa(int(wd)) + "," + } + result = strings.TrimRight(result, ",") + return +} + +func (wds *WeekDays) Restore(input string) error { + for _, wd := range strings.Split(input, ",") { + if wd != "" { + mm, _ := strconv.Atoi(wd) + *wds = append(*wds, time.Weekday(mm)) + } + } + return nil +} + +func (mds MonthDays) Store() (result string, err error) { + for _, md := range mds { + result += strconv.Itoa(int(md)) + "," + } + result = strings.TrimRight(result, ",") + return +} + +func (mds *MonthDays) Restore(input string) error { + for _, md := range strings.Split(input, ",") { + if md != "" { + mm, _ := strconv.Atoi(md) + *mds = append(*mds, mm) + } + } + return nil +} + +func (ms Months) Store() (result string, err error) { + for _, m := range ms { + result += strconv.Itoa(int(m)) + "," + } + result = strings.TrimRight(result, ",") + return +} + +func (ms *Months) Restore(input string) error { + for _, m := range strings.Split(input, ",") { + if m != "" { + mm, _ := strconv.Atoi(m) + *ms = append(*ms, time.Month(mm)) + } + } + return nil +} + +func (yss Years) Store() (result string, err error) { + for _, ys := range yss { + result += strconv.Itoa(int(ys)) + "," + } + result = strings.TrimRight(result, ",") + return +} + +func (yss *Years) Restore(input string) error { + for _, ys := range strings.Split(input, ",") { + if ys != "" { + mm, _ := strconv.Atoi(ys) + *yss = append(*yss, mm) + } + } + return nil +} diff --git a/rater/simple_serializer_test.go b/rater/simple_serializer_test.go new file mode 100644 index 000000000..1ca0f3229 --- /dev/null +++ b/rater/simple_serializer_test.go @@ -0,0 +1,218 @@ +/* +Rating system designed to be used in VoIP Carriers World +Copyright (C) 2013 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 rater + +import ( + "reflect" + "testing" + "time" +) + +func TestSimpleMarshallerApStoreRestore(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) + result, err := ap.Store() + expected := "1328106601000000000|;2;1;3,4;14:30:00;15:00:00;0;0;0;0;0" + if err != nil || !reflect.DeepEqual(result, expected) { + t.Errorf("Expected %q was %q", expected, result) + } + ap1 := &ActivationPeriod{} + err = ap1.Restore(result) + if err != nil || !reflect.DeepEqual(ap, ap1) { + t.Errorf("Expected %v was %v: %v", ap, ap1, err) + } +} + +func TestSimpleMarshallerApRestoreFromString(t *testing.T) { + s := "1325376000000000000|;1,2,3,4,5,6,7,8,9,10,11,12;1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31;1,2,3,4,5,6,0;00:00:00;;10;0;0.2;60;1\n" + ap := &ActivationPeriod{} + err := ap.Restore(s) + if err != nil || len(ap.Intervals) != 1 { + t.Error("Error restoring activation period from string", ap) + } +} + +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, err := rp.Store() + expected := "test>0723=1328106601000000000|;2;1;3,4;14:30:00;15:00:00;0;0;0;0;0" + if err != nil || !reflect.DeepEqual(result, expected) { + t.Errorf("Expected %q was %q", expected, result) + } + rp1 := &RatingProfile{} + err = rp1.Restore(result) + if err != nil || !reflect.DeepEqual(rp, rp1) { + t.Errorf("Expected %v was %v", rp, rp1) + } +} + +func TestActionTimingStoreRestore(t *testing.T) { + i := &Interval{ + Months: Months{time.January, time.February, time.March, time.April, time.May, time.June, time.July, time.August, time.September, time.October, time.November, time.December}, + MonthDays: MonthDays{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, + WeekDays: WeekDays{time.Monday, time.Tuesday, time.Wednesday, time.Thursday, time.Friday}, + StartTime: "18:00:00", + EndTime: "00:00:00", + Weight: 10.0, + ConnectFee: 0.0, + Price: 1.0, + PricedUnits: 60, + RateIncrements: 1, + } + at := &ActionTiming{ + Id: "some uuid", + Tag: "test", + UserBalanceIds: []string{"one", "two", "three"}, + Timing: i, + Weight: 10.0, + ActionsId: "Commando", + } + r, err := at.Store() + if err != nil || r != "some uuid|test|one,two,three|;1,2,3,4,5,6,7,8,9,10,11,12;1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31;1,2,3,4,5;18:00:00;00:00:00;10;0;1;60;1|10|Commando" { + t.Errorf("Error serializing action timing: %v", string(r)) + } + o := &ActionTiming{} + err = o.Restore(r) + if err != nil || !reflect.DeepEqual(o, at) { + t.Errorf("Expected %v was %v", at, o) + } +} + +func TestActionTriggerStoreRestore(t *testing.T) { + at := &ActionTrigger{ + Id: "some_uuid", + BalanceId: CREDIT, + Direction: OUTBOUND, + ThresholdValue: 100.0, + DestinationId: "NAT", + Weight: 10.0, + ActionsId: "Commando", + } + r, err := at.Store() + if err != nil || r != "some_uuid;MONETARY;OUT;NAT;Commando;100;10;false" { + t.Errorf("Error serializing action trigger: %v", string(r)) + } + o := &ActionTrigger{} + err = o.Restore(r) + if err != nil || !reflect.DeepEqual(o, at) { + t.Errorf("Expected %v was %v", at, o) + } +} + +func TestIntervalStoreRestore(t *testing.T) { + i := &Interval{ + Months: Months{time.January, time.February, time.March, time.April, time.May, time.June, time.July, time.August, time.September, time.October, time.November, time.December}, + MonthDays: MonthDays{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, + WeekDays: WeekDays{time.Monday, time.Tuesday, time.Wednesday, time.Thursday, time.Friday}, + StartTime: "18:00:00", + EndTime: "00:00:00", + Weight: 10.0, + ConnectFee: 0.0, + Price: 1.0, + PricedUnits: 60, + RateIncrements: 1, + } + r, err := i.Store() + if err != nil || r != ";1,2,3,4,5,6,7,8,9,10,11,12;1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31;1,2,3,4,5;18:00:00;00:00:00;10;0;1;60;1" { + t.Errorf("Error serializing interval: %v", string(r)) + } + o := &Interval{} + err = o.Restore(r) + if err != nil || !reflect.DeepEqual(o, i) { + t.Errorf("Expected %v was %v", i, o) + } +} + +func TestIntervalRestoreFromString(t *testing.T) { + s := ";1,2,3,4,5,6,7,8,9,10,11,12;1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31;1,2,3,4,5,6,0;00:00:00;;10;0;0.2;60;1" + i := Interval{} + err := i.Restore(s) + if err != nil || i.Price != 0.2 { + t.Errorf("Error restoring inteval period from string %+v", i) + } +} + +func TestMonthYearStoreRestore(t *testing.T) { + y := Years{2010, 2011, 2012} + r, err := y.Store() + if err != nil || r != "2010,2011,2012" { + t.Errorf("Error serializing years: %v", string(r)) + } + o := Years{} + err = o.Restore(r) + if err != nil || !reflect.DeepEqual(o, y) { + t.Errorf("Expected %v was %v", y, o) + } +} + +func TestMonthStoreRestore(t *testing.T) { + m := Months{5, 6, 7, 8} + r, err := m.Store() + if err != nil || r != "5,6,7,8" { + t.Errorf("Error serializing months: %v", string(r)) + } + o := Months{} + err = o.Restore(r) + if err != nil || !reflect.DeepEqual(o, m) { + t.Errorf("Expected %v was %v", m, o) + } +} + +func TestMonthDayStoreRestore(t *testing.T) { + md := MonthDays{24, 25, 26} + r, err := md.Store() + if err != nil || r != "24,25,26" { + t.Errorf("Error serializing month days: %v", string(r)) + } + o := MonthDays{} + err = o.Restore(r) + if err != nil || !reflect.DeepEqual(o, md) { + t.Errorf("Expected %v was %v", md, o) + } +} + +func TestWeekDayStoreRestore(t *testing.T) { + wd := WeekDays{time.Saturday, time.Sunday} + r, err := wd.Store() + if err != nil || r != "6,0" { + t.Errorf("Error serializing week days: %v", string(r)) + } + o := WeekDays{} + err = o.Restore(r) + if err != nil || !reflect.DeepEqual(o, wd) { + t.Errorf("Expected %v was %v", wd, o) + } +} diff --git a/rater/storage_interface.go b/rater/storage_interface.go index 876b65a2d..16b811d7b 100644 --- a/rater/storage_interface.go +++ b/rater/storage_interface.go @@ -25,7 +25,6 @@ import ( gmsgpack "github.com/ugorji/go-msgpack" "github.com/vmihailenco/msgpack" "labix.org/v2/mgo/bson" - "strings" ) const ( @@ -145,71 +144,15 @@ func (gm *GOBMarshaler) Unmarshal(data []byte, v interface{}) error { return gob.NewDecoder(bytes.NewBuffer(data)).Decode(v) } -type storer interface { - store() string - restore(string) +type MyMarshaler struct{} + +func (mm *MyMarshaler) Marshal(v interface{}) ([]byte, error) { + ser := v.(Serializer) + res, err := ser.Store() + return []byte(res), err } -type MyMarshaler struct { - buf bytes.Buffer -} - -func (mm *MyMarshaler) Marshal(v interface{}) (data []byte, err error) { - switch v.(type) { - case []*Action: - result := "" - for _, a := range v.([]*Action) { - result += a.store() + "~" - } - result = strings.TrimRight(result, "~") - return []byte(result), nil - case []*ActionTiming: - result := "" - for _, at := range v.([]*ActionTiming) { - result += at.store() + "~" - } - result = strings.TrimRight(result, "~") - return []byte(result), nil - case storer: - s := v.(storer) - return []byte(s.store()), nil - } - mm.buf.Reset() - if err = json.NewEncoder(&mm.buf).Encode(v); err == nil { - data = mm.buf.Bytes() - } - return -} - -func (mm *MyMarshaler) Unmarshal(data []byte, v interface{}) (err error) { - switch v.(type) { - case *[]*Action: - as := v.(*[]*Action) - for _, a_string := range strings.Split(string(data), "~") { - if len(a_string) > 0 { - a := &Action{} - a.restore(a_string) - *as = append(*as, a) - } - } - return nil - case *[]*ActionTiming: - ats := v.(*[]*ActionTiming) - for _, at_string := range strings.Split(string(data), "~") { - if len(at_string) > 0 { - at := &ActionTiming{} - at.restore(at_string) - *ats = append(*ats, at) - } - } - return nil - case storer: - s := v.(storer) - s.restore(string(data)) - return nil - - } - mm.buf.Reset() - mm.buf.Write(data) - return json.NewDecoder(&mm.buf).Decode(v) +func (mm *MyMarshaler) Unmarshal(data []byte, v interface{}) error { + ser := v.(Serializer) + return ser.Restore(string(data)) } diff --git a/rater/units_counter.go b/rater/units_counter.go index db05d504b..ae59202bf 100644 --- a/rater/units_counter.go +++ b/rater/units_counter.go @@ -20,8 +20,6 @@ package rater import ( "fmt" - "strconv" - "strings" ) // Amount of a trafic of a certain type @@ -67,35 +65,3 @@ func (uc *UnitsCounter) addMinutes(amount float64, prefix string) { func (uc *UnitsCounter) String() string { return fmt.Sprintf("%s %s %v", uc.BalanceId, uc.Direction, uc.Units) } - -/* -Serializes the unit counter for the storage. Used for key-value storages. -*/ -func (uc *UnitsCounter) store() (result string) { - result += uc.Direction + "/" - result += uc.BalanceId + "/" - result += strconv.FormatFloat(uc.Units, 'f', -1, 64) + "/" - for _, mb := range uc.MinuteBuckets { - result += mb.store() + "," - } - result = strings.TrimRight(result, ",") - return -} - -/* -De-serializes the unit counter for the storage. Used for key-value storages. -*/ -func (uc *UnitsCounter) restore(input string) { - elements := strings.Split(input, "/") - if len(elements) != 4 { - return - } - uc.Direction = elements[0] - uc.BalanceId = elements[1] - uc.Units, _ = strconv.ParseFloat(elements[2], 64) - for _, mbs := range strings.Split(elements[3], ",") { - mb := &MinuteBucket{} - mb.restore(mbs) - uc.MinuteBuckets = append(uc.MinuteBuckets, mb) - } -} diff --git a/rater/units_counter_test.go b/rater/units_counter_test.go index 32958dc83..059a2866b 100644 --- a/rater/units_counter_test.go +++ b/rater/units_counter_test.go @@ -30,13 +30,13 @@ func TestUnitsCounterStoreRestore(t *testing.T) { Units: 100, MinuteBuckets: []*MinuteBucket{&MinuteBucket{Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}}, } - r := uc.store() - if string(r) != "OUT/SMS/100/0;20;1;0;NAT,0;10;10;0;RET" { + r, err := uc.Store() + if err != nil || r != "OUT/SMS/100/0;20;1;0;NAT,0;10;10;0;RET" { t.Errorf("Error serializing units counter: %v", string(r)) } o := &UnitsCounter{} - o.restore(r) - if !reflect.DeepEqual(o, uc) { + err = o.Restore(r) + if err != nil || !reflect.DeepEqual(o, uc) { t.Errorf("Expected %v was %v", uc, o) } } diff --git a/rater/userbalance.go b/rater/userbalance.go index 18b49cebd..332d8360a 100644 --- a/rater/userbalance.go +++ b/rater/userbalance.go @@ -21,8 +21,6 @@ package rater import ( "errors" //"log" - "strconv" - "strings" ) const ( @@ -274,75 +272,3 @@ func (ub *UserBalance) initMinuteCounters() { } } } - -/* -Serializes the user balance for the storage. Used for key-value storages. -*/ -func (ub *UserBalance) store() (result string) { - result += ub.Id + "|" - result += ub.Type + "|" - for k, v := range ub.BalanceMap { - result += k + ":" + strconv.FormatFloat(v, 'f', -1, 64) + "#" - } - result = strings.TrimRight(result, "#") + "|" - for _, mb := range ub.MinuteBuckets { - result += mb.store() + "#" - } - result = strings.TrimRight(result, "#") + "|" - for _, uc := range ub.UnitCounters { - result += uc.store() + "#" - } - result = strings.TrimRight(result, "#") + "|" - for _, at := range ub.ActionTriggers { - result += at.store() + "#" - } - result = strings.TrimRight(result, "#") - return -} - -/* -De-serializes the user balance for the storage. Used for key-value storages. -*/ -func (ub *UserBalance) restore(input string) { - elements := strings.Split(input, "|") - if len(elements) < 2 { - return - } - ub.Id = elements[0] - ub.Type = elements[1] - if ub.BalanceMap == nil { - ub.BalanceMap = make(map[string]float64, 0) - } - for _, maps := range strings.Split(elements[2], "#") { - kv := strings.Split(maps, ":") - if len(kv) != 2 { - continue - } - value, _ := strconv.ParseFloat(kv[1], 64) - ub.BalanceMap[kv[0]] = value - } - for _, mbs := range strings.Split(elements[3], "#") { - if mbs == "" { - continue - } - mb := &MinuteBucket{} - mb.restore(mbs) - ub.MinuteBuckets = append(ub.MinuteBuckets, mb) - } - for _, ucs := range strings.Split(elements[4], "#") { - if ucs == "" { - continue - } - uc := &UnitsCounter{} - uc.restore(ucs) - ub.UnitCounters = append(ub.UnitCounters, uc) - } - for _, ats := range strings.Split(elements[5], "#") { - if ats == "" { - continue - } - at := &ActionTrigger{} - at.restore(ats) - ub.ActionTriggers = append(ub.ActionTriggers, at) - } -} diff --git a/rater/userbalance_test.go b/rater/userbalance_test.go index 23b036a0f..cbfe1a25b 100644 --- a/rater/userbalance_test.go +++ b/rater/userbalance_test.go @@ -70,14 +70,14 @@ func TestUserBalanceStoreRestore(t *testing.T) { UnitCounters: []*UnitsCounter{uc, uc}, ActionTriggers: ActionTriggerPriotityList{at, at, at}, } - r := ub.store() - if string(r) != "rif|postpaid|SMSOUT:14#INTERNETOUT:1024|0;20;1;0;NAT#0;10;10;0;RET|OUT/SMS/100/0;20;1;0;NAT,0;10;10;0;RET#OUT/SMS/100/0;20;1;0;NAT,0;10;10;0;RET|some_uuid;MONETARY;OUT;NAT;Commando;100;10;false#some_uuid;MONETARY;OUT;NAT;Commando;100;10;false#some_uuid;MONETARY;OUT;NAT;Commando;100;10;false" && - string(r) != "rif|postpaid|INTERNETOUT:1024#SMSOUT:14|0;20;1;0;NAT#0;10;10;0;RET|OUT/SMS/100/0;20;1;0;NAT,0;10;10;0;RET#OUT/SMS/100/0;20;1;0;NAT,0;10;10;0;RET|some_uuid;MONETARY;OUT;NAT;Commando;100;10;false#some_uuid;MONETARY;OUT;NAT;Commando;100;10;false#some_uuid;MONETARY;OUT;NAT;Commando;100;10;false" { + r, err := ub.Store() + if err != nil || r != "rif|postpaid|SMSOUT:14#INTERNETOUT:1024|0;20;1;0;NAT#0;10;10;0;RET|OUT/SMS/100/0;20;1;0;NAT,0;10;10;0;RET#OUT/SMS/100/0;20;1;0;NAT,0;10;10;0;RET|some_uuid;MONETARY;OUT;NAT;Commando;100;10;false#some_uuid;MONETARY;OUT;NAT;Commando;100;10;false#some_uuid;MONETARY;OUT;NAT;Commando;100;10;false" && + r != "rif|postpaid|INTERNETOUT:1024#SMSOUT:14|0;20;1;0;NAT#0;10;10;0;RET|OUT/SMS/100/0;20;1;0;NAT,0;10;10;0;RET#OUT/SMS/100/0;20;1;0;NAT,0;10;10;0;RET|some_uuid;MONETARY;OUT;NAT;Commando;100;10;false#some_uuid;MONETARY;OUT;NAT;Commando;100;10;false#some_uuid;MONETARY;OUT;NAT;Commando;100;10;false" { t.Errorf("Error serializing action timing: %v", string(r)) } o := &UserBalance{} - o.restore(r) - if !reflect.DeepEqual(o, ub) { + err = o.Restore(r) + if err != nil || !reflect.DeepEqual(o, ub) { t.Errorf("Expected %v was %v", ub, o) } }