From f3dbbf77fceef9818b315704ec3c7bbe93adaf5b Mon Sep 17 00:00:00 2001 From: DanB Date: Wed, 10 Jul 2013 16:58:57 +0200 Subject: [PATCH] Adding TPDestinationRate APIs --- apier/tpdestinationrates.go | 84 +++++++++++++++++++ apier/tprates.go | 2 +- .../mysql/create_tariffplan_tables.sql | 3 +- rater/storage_interface.go | 4 + rater/storage_map.go | 17 ++++ rater/storage_mongo.go | 16 ++++ rater/storage_redis.go | 16 ++++ rater/storage_sql.go | 74 +++++++++++++++- utils/{data_tprates.go => tpdata.go} | 16 +++- 9 files changed, 228 insertions(+), 4 deletions(-) create mode 100644 apier/tpdestinationrates.go rename utils/{data_tprates.go => tpdata.go} (76%) diff --git a/apier/tpdestinationrates.go b/apier/tpdestinationrates.go new file mode 100644 index 000000000..9d96eedcf --- /dev/null +++ b/apier/tpdestinationrates.go @@ -0,0 +1,84 @@ +/* +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 apier + +// This file deals with tp_destination_rates management over APIs + +import ( + "errors" + "fmt" + "github.com/cgrates/cgrates/utils" +) + + +// Creates a new DestinationRate profile within a tariff plan +func (self *Apier) SetTPDestinationRate(attrs utils.TPDestinationRate, reply *string) error { + if missing := utils.MissingStructFields(&attrs, []string{"TPid", "DestinationRateId", "DestinationRates"}); len(missing) != 0 { + return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) + } + if exists, err := self.StorDb.ExistsTPDestinationRate(attrs.TPid, attrs.DestinationRateId); err != nil { + return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) + } else if exists { + return errors.New(utils.ERR_DUPLICATE) + } + if err := self.StorDb.SetTPDestinationRate(&attrs); err != nil { + return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) + } + *reply = "OK" + return nil +} + +type AttrGetTPDestinationRate struct { + TPid string // Tariff plan id + DestinationRateId string // Rate id +} + +// Queries specific DestinationRate profile on tariff plan +func (self *Apier) GetTPDestinationRate(attrs AttrGetTPDestinationRate, reply *utils.TPDestinationRate) error { + if missing := utils.MissingStructFields(&attrs, []string{"TPid", "DestinationRateId"}); len(missing) != 0 { //Params missing + return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) + } + if dr, err := self.StorDb.GetTPDestinationRate(attrs.TPid, attrs.DestinationRateId); err != nil { + return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) + } else if dr == nil { + return errors.New(utils.ERR_NOT_FOUND) + } else { + *reply = *dr + } + return nil +} + +type AttrTPDestinationRateIds struct { + TPid string // Tariff plan id +} + +// Queries DestinationRate identities on specific tariff plan. +func (self *Apier) GetTPDestinationRateIds(attrs AttrGetTPRateIds, reply *[]string) error { + if missing := utils.MissingStructFields(&attrs, []string{"TPid"}); len(missing) != 0 { //Params missing + return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) + } + if ids, err := self.StorDb.GetTPDestinationRateIds(attrs.TPid); err != nil { + return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) + } else if ids == nil { + return errors.New(utils.ERR_NOT_FOUND) + } else { + *reply = ids + } + return nil +} diff --git a/apier/tprates.go b/apier/tprates.go index eeb0a4724..56b9c8595 100644 --- a/apier/tprates.go +++ b/apier/tprates.go @@ -28,7 +28,7 @@ import ( // Creates a new rate within a tariff plan func (self *Apier) SetTPRate(attrs utils.TPRate, reply *string) error { - if missing := utils.MissingStructFields(&attrs, []string{"TPid", "RateId", "ConnectFee", "RateSlots"}); len(missing) != 0 { + if missing := utils.MissingStructFields(&attrs, []string{"TPid", "RateId", "RateSlots"}); len(missing) != 0 { return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) } if exists, err := self.StorDb.ExistsTPRate(attrs.TPid, attrs.RateId); err != nil { diff --git a/data/storage/mysql/create_tariffplan_tables.sql b/data/storage/mysql/create_tariffplan_tables.sql index 4301c1fdc..b05d8fe3e 100644 --- a/data/storage/mysql/create_tariffplan_tables.sql +++ b/data/storage/mysql/create_tariffplan_tables.sql @@ -58,7 +58,8 @@ CREATE TABLE `tp_destination_rates` ( `destinations_tag` varchar(24) NOT NULL, `rates_tag` varchar(24) NOT NULL, PRIMARY KEY (`id`), - KEY `tpid` (`tpid`) + KEY `tpid` (`tpid`), + UNIQUE KEY `tpid_tag_dst_rates` (`tpid`,`tag`,`destinations_tag`, `rates_tag`) ); -- diff --git a/rater/storage_interface.go b/rater/storage_interface.go index 3dad396f3..14e18a344 100644 --- a/rater/storage_interface.go +++ b/rater/storage_interface.go @@ -71,6 +71,10 @@ type DataStorage interface { SetTPRate(*utils.TPRate) error GetTPRate(string, string) (*utils.TPRate, error) GetTPRateIds(string) ([]string, error) + ExistsTPDestinationRate(string, string) (bool, error) + SetTPDestinationRate(*utils.TPDestinationRate) error + GetTPDestinationRate(string, string) (*utils.TPDestinationRate, error) + GetTPDestinationRateIds(string) ([]string, error) // End Apier functions GetActions(string) (Actions, error) SetActions(string, Actions) error diff --git a/rater/storage_map.go b/rater/storage_map.go index 432e51336..039d2dc4f 100644 --- a/rater/storage_map.go +++ b/rater/storage_map.go @@ -125,6 +125,23 @@ func (ms *MapStorage) GetTPRateIds(tpid string) ([]string, error) { return nil, errors.New(utils.ERR_NOT_IMPLEMENTED) } +func (ms *MapStorage) ExistsTPDestinationRate(tpid, drId string) (bool, error) { + return false, errors.New(utils.ERR_NOT_IMPLEMENTED) +} + +func (ms *MapStorage) SetTPDestinationRate(dr *utils.TPDestinationRate) error { + return errors.New(utils.ERR_NOT_IMPLEMENTED) +} + +func (ms *MapStorage) GetTPDestinationRate(tpid, drId string) (*utils.TPDestinationRate, error) { + return nil, nil +} + +func (ms *MapStorage) GetTPDestinationRateIds(tpid string) ([]string, error) { + return nil, errors.New(utils.ERR_NOT_IMPLEMENTED) +} + + func (ms *MapStorage) GetActions(key string) (as Actions, err error) { if values, ok := ms.dict[ACTION_PREFIX+key]; ok { err = ms.ms.Unmarshal(values, &as) diff --git a/rater/storage_mongo.go b/rater/storage_mongo.go index b8519cb00..73bf564b9 100644 --- a/rater/storage_mongo.go +++ b/rater/storage_mongo.go @@ -200,6 +200,22 @@ func (ms *MongoStorage) GetTPRateIds(tpid string) ([]string, error) { return nil, errors.New(utils.ERR_NOT_IMPLEMENTED) } +func (ms *MongoStorage) ExistsTPDestinationRate(tpid, drId string) (bool, error) { + return false, errors.New(utils.ERR_NOT_IMPLEMENTED) +} + +func (ms *MongoStorage) SetTPDestinationRate(dr *utils.TPDestinationRate) error { + return errors.New(utils.ERR_NOT_IMPLEMENTED) +} + +func (ms *MongoStorage) GetTPDestinationRate(tpid, drId string) (*utils.TPDestinationRate, error) { + return nil, nil +} + +func (ms *MongoStorage) GetTPDestinationRateIds(tpid string) ([]string, error) { + return nil, errors.New(utils.ERR_NOT_IMPLEMENTED) +} + func (ms *MongoStorage) GetActions(key string) (as Actions, err error) { result := AcKeyValue{} err = ms.db.C("actions").Find(bson.M{"key": key}).One(&result) diff --git a/rater/storage_redis.go b/rater/storage_redis.go index 3bdc78242..1ba05466d 100644 --- a/rater/storage_redis.go +++ b/rater/storage_redis.go @@ -155,6 +155,22 @@ func (rs *RedisStorage) GetTPRateIds(tpid string) ([]string, error) { return nil, errors.New(utils.ERR_NOT_IMPLEMENTED) } +func (rs *RedisStorage) ExistsTPDestinationRate(tpid, drId string) (bool, error) { + return false, errors.New(utils.ERR_NOT_IMPLEMENTED) +} + +func (rs *RedisStorage) SetTPDestinationRate(dr *utils.TPDestinationRate) error { + return errors.New(utils.ERR_NOT_IMPLEMENTED) +} + +func (rs *RedisStorage) GetTPDestinationRate(tpid, drId string) (*utils.TPDestinationRate, error) { + return nil, nil +} + +func (rs *RedisStorage) GetTPDestinationRateIds(tpid string) ([]string, error) { + return nil, errors.New(utils.ERR_NOT_IMPLEMENTED) +} + func (rs *RedisStorage) GetActions(key string) (as Actions, err error) { var values string if values, err = rs.db.Get(ACTION_PREFIX + key); err == nil { diff --git a/rater/storage_sql.go b/rater/storage_sql.go index d0468f0df..a7871ab75 100644 --- a/rater/storage_sql.go +++ b/rater/storage_sql.go @@ -22,7 +22,6 @@ import ( "database/sql" "encoding/json" "fmt" - //"errors" "github.com/cgrates/cgrates/utils" ) @@ -267,6 +266,79 @@ func (self *SQLStorage) GetTPRateIds(tpid string) ([]string, error) { return ids, nil } +func (self *SQLStorage) ExistsTPDestinationRate(tpid, drId string) (bool, error) { + var exists bool + err := self.Db.QueryRow(fmt.Sprintf("SELECT EXISTS (SELECT 1 FROM %s WHERE tpid='%s' AND tag='%s')", utils.TBL_TP_DESTINATION_RATES, tpid, drId)).Scan(&exists) + if err != nil { + return false, err + } + return exists, nil +} + +func (self *SQLStorage) SetTPDestinationRate(dr *utils.TPDestinationRate) error { + if len(dr.DestinationRates) == 0 { + return nil //Nothing to set + } + // Using multiple values in query to spare some network processing time + qry := fmt.Sprintf("INSERT INTO %s (tpid, tag, destinations_tag, rates_tag) VALUES", utils.TBL_TP_DESTINATION_RATES) + for idx, drPair := range dr.DestinationRates { + if idx!=0 { //Consecutive values after the first will be prefixed with "," as separator + qry += "," + } + qry += fmt.Sprintf("('%s','%s','%s','%s')", dr.TPid, dr.DestinationRateId, drPair.DestinationId, drPair.RateId) + } + if _, err := self.Db.Exec(qry); err != nil { + return err + } + return nil +} + +func (self *SQLStorage) GetTPDestinationRate(tpid, drId string) (*utils.TPDestinationRate, error) { + rows, err := self.Db.Query(fmt.Sprintf("SELECT destinations_tag, rates_tag FROM %s WHERE tpid='%s' AND tag='%s'", utils.TBL_TP_DESTINATION_RATES, tpid, drId)) + if err != nil { + return nil, err + } + defer rows.Close() + dr := &utils.TPDestinationRate{TPid: tpid, DestinationRateId: drId} + i := 0 + for rows.Next() { + i++ //Keep here a reference so we know we got at least one prefix + var dstTag, ratesTag string + err = rows.Scan(&dstTag, &ratesTag) + if err != nil { + return nil, err + } + dr.DestinationRates = append(dr.DestinationRates, utils.DestinationRate{dstTag, ratesTag}) + } + if i == 0 { + return nil, nil + } + return dr, nil +} + +func (self *SQLStorage) GetTPDestinationRateIds(tpid string) ([]string, error) { + rows, err := self.Db.Query(fmt.Sprintf("SELECT DISTINCT tag FROM %s where tpid='%s'", utils.TBL_TP_DESTINATION_RATES, tpid)) + if err != nil { + return nil, err + } + defer rows.Close() + ids := []string{} + i := 0 + for rows.Next() { + i++ //Keep here a reference so we know we got at least one + var id string + err = rows.Scan(&id) + if err != nil { + return nil, err + } + ids = append(ids, id) + } + if i == 0 { + return nil, nil + } + return ids, nil +} + func (self *SQLStorage) GetActions(string) (as Actions, err error) { return } diff --git a/utils/data_tprates.go b/utils/tpdata.go similarity index 76% rename from utils/data_tprates.go rename to utils/tpdata.go index 5a41757cc..784956d63 100644 --- a/utils/data_tprates.go +++ b/utils/tpdata.go @@ -18,7 +18,7 @@ along with this program. If not, see package utils -// This file deals with tp_rates data definition +// This file deals with tp_* data definition type TPRate struct { TPid string // Tariff plan id @@ -33,3 +33,17 @@ type RateSlot struct { RateIncrements int // This rate will apply in increments of duration Weight float64 // Rate's priority when dealing with grouped rates } + + +type TPDestinationRate struct { + TPid string // Tariff plan id + DestinationRateId string // DestinationRate profile id + DestinationRates []DestinationRate // Set of destinationid-rateid bindings +} + +type DestinationRate struct { + DestinationId string // The destination identity + RateId string // The rate identity +} + +