From 092a80d9d4ec1503ca7d681f29881ec192b5b297 Mon Sep 17 00:00:00 2001 From: DanB Date: Fri, 19 Jul 2013 09:37:07 +0200 Subject: [PATCH] Adding ActionTimings api methods --- apier/tpactiontimings.go | 97 ++++++++++++++++++++++++++ docs/apicalls.rst | 2 +- rater/storage_interface.go | 4 ++ rater/storage_map.go | 16 +++++ rater/storage_mongo.go | 16 +++++ rater/storage_redis.go | 16 +++++ rater/storage_sql.go | 88 ++++++++++++++++++++++-- utils/apitpdata.go | 137 +++++++++++++++++++++++++++++++++++++ utils/tpdata.go | 113 +----------------------------- 9 files changed, 371 insertions(+), 118 deletions(-) create mode 100644 apier/tpactiontimings.go create mode 100644 utils/apitpdata.go diff --git a/apier/tpactiontimings.go b/apier/tpactiontimings.go new file mode 100644 index 000000000..802e53b9f --- /dev/null +++ b/apier/tpactiontimings.go @@ -0,0 +1,97 @@ +/* +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 + +import ( + "errors" + "fmt" + "github.com/cgrates/cgrates/utils" +) + +// Creates a new ActionTimings profile within a tariff plan +func (self *Apier) SetTPActionTimings(attrs utils.ApiTPActionTimings, reply *string) error { + if missing := utils.MissingStructFields(&attrs, []string{"TPid", "ActionsId", "ActionTimings"}); len(missing) != 0 { + return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) + } + for _, at := range attrs.ActionTimings { + requiredFields := []string{"ActionsId", "TimingId", "Weight"} + if missing := utils.MissingStructFields(&at, requiredFields); len(missing) != 0 { + return fmt.Errorf("%s:Action:%s:%v", utils.ERR_MANDATORY_IE_MISSING, at.ActionsId, missing) + } + } + if exists, err := self.StorDb.ExistsTPActionTimings(attrs.TPid, attrs.ActionTimingsId); err != nil { + return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) + } else if exists { + return errors.New(utils.ERR_DUPLICATE) + } + ats := make(map[string][]*utils.TPActionTimingsRow, 1) // Only one id will be stored in the map + for _,at := range attrs.ActionTimings { + ats[attrs.ActionTimingsId] = append( ats[attrs.ActionTimingsId], &utils.TPActionTimingsRow{at.ActionsId, at.TimingId, at.Weight} ) + } + if err := self.StorDb.SetTPActionTimings(attrs.TPid, ats); err != nil { + return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) + } + *reply = "OK" + return nil +} + +type AttrGetTPActionTimings struct { + TPid string // Tariff plan id + ActionTimingsId string // ActionTimings id +} + +// Queries specific ActionTimings profile on tariff plan +func (self *Apier) GetTPActionTimings(attrs AttrGetTPActionTimings, reply *utils.ApiTPActionTimings) error { + if missing := utils.MissingStructFields(&attrs, []string{"TPid", "ActionTimingsId"}); len(missing) != 0 { //Params missing + return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) + } + if ats, err := self.StorDb.GetTPActionTimings(attrs.TPid, attrs.ActionTimingsId); err != nil { + return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) + } else if len(ats) == 0 { + return errors.New(utils.ERR_NOT_FOUND) + } else { // Got the data we need, convert it from []TPActionTimingsRow into ApiTPActionTimings + atRply := &utils.ApiTPActionTimings{ attrs.TPid, attrs.ActionTimingsId, make([]utils.ApiActionTiming, len(ats[attrs.ActionTimingsId])) } + for idx,row := range ats[attrs.ActionTimingsId] { + atRply.ActionTimings[idx] = utils.ApiActionTiming{ row.ActionsId, row.TimingId, row.Weight } + } + *reply = *atRply + } + return nil +} + +type AttrGetTPActionTimingIds struct { + TPid string // Tariff plan id +} + +// Queries ActionTimings identities on specific tariff plan. +func (self *Apier) GetTPActionTimingIds(attrs AttrGetTPActionTimingIds, 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.GetTPActionTimingIds(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/docs/apicalls.rst b/docs/apicalls.rst index 401a337ea..4e3781e6b 100644 --- a/docs/apicalls.rst +++ b/docs/apicalls.rst @@ -189,7 +189,7 @@ Actions .. toctree:: :maxdepth: 2 - api_actions + api_tpactions 6.1.5. Management API diff --git a/rater/storage_interface.go b/rater/storage_interface.go index a28054afa..17e5bbd1a 100644 --- a/rater/storage_interface.go +++ b/rater/storage_interface.go @@ -87,6 +87,10 @@ type DataStorage interface { SetTPActions(*utils.TPActions) error GetTPActions(string, string) (*utils.TPActions, error) GetTPActionIds(string) ([]string, error) + ExistsTPActionTimings(string, string) (bool, error) + SetTPActionTimings(string, map[string][]*utils.TPActionTimingsRow) error + GetTPActionTimings(string, string) (map[string][]*utils.TPActionTimingsRow, error) + GetTPActionTimingIds(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 c3672ba90..ad26d0887 100644 --- a/rater/storage_map.go +++ b/rater/storage_map.go @@ -189,6 +189,22 @@ func (ms *MapStorage) GetTPActionIds(tpid string) ([]string, error) { return nil, errors.New(utils.ERR_NOT_IMPLEMENTED) } +func (ms *MapStorage) ExistsTPActionTimings(tpid, atId string) (bool, error) { + return false, errors.New(utils.ERR_NOT_IMPLEMENTED) +} + +func (ms *MapStorage) SetTPActionTimings(tpid string, ats map[string][]*utils.TPActionTimingsRow) error { + return errors.New(utils.ERR_NOT_IMPLEMENTED) +} + +func (ms *MapStorage) GetTPActionTimings(tpid, atId string) (map[string][]*utils.TPActionTimingsRow, error) { + return nil, errors.New(utils.ERR_NOT_IMPLEMENTED) +} + +func (ms *MapStorage) GetTPActionTimingIds(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 1fbcab6a3..53870a070 100644 --- a/rater/storage_mongo.go +++ b/rater/storage_mongo.go @@ -264,6 +264,22 @@ func (ms *MongoStorage) GetTPActionIds(tpid string) ([]string, error) { return nil, errors.New(utils.ERR_NOT_IMPLEMENTED) } +func (ms *MongoStorage) ExistsTPActionTimings(tpid, atId string) (bool, error) { + return false, errors.New(utils.ERR_NOT_IMPLEMENTED) +} + +func (ms *MongoStorage) SetTPActionTimings(tpid string, ats map[string][]*utils.TPActionTimingsRow) error { + return errors.New(utils.ERR_NOT_IMPLEMENTED) +} + +func (ms *MongoStorage) GetTPActionTimings(tpid, atId string) (map[string][]*utils.TPActionTimingsRow, error) { + return nil, errors.New(utils.ERR_NOT_IMPLEMENTED) +} + +func (ms *MongoStorage) GetTPActionTimingIds(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 b97de290d..3009ad410 100644 --- a/rater/storage_redis.go +++ b/rater/storage_redis.go @@ -219,6 +219,22 @@ func (rs *RedisStorage) GetTPActionIds(tpid string) ([]string, error) { return nil, errors.New(utils.ERR_NOT_IMPLEMENTED) } +func (rs *RedisStorage) ExistsTPActionTimings(tpid, atId string) (bool, error) { + return false, errors.New(utils.ERR_NOT_IMPLEMENTED) +} + +func (rs *RedisStorage) SetTPActionTimings(tpid string, ats map[string][]*utils.TPActionTimingsRow) error { + return errors.New(utils.ERR_NOT_IMPLEMENTED) +} + +func (rs *RedisStorage) GetTPActionTimings(tpid, atId string) (map[string][]*utils.TPActionTimingsRow, error) { + return nil, errors.New(utils.ERR_NOT_IMPLEMENTED) +} + +func (rs *RedisStorage) GetTPActionTimingIds(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 c76dca64e..dafefc9e0 100644 --- a/rater/storage_sql.go +++ b/rater/storage_sql.go @@ -586,16 +586,92 @@ func (self *SQLStorage) GetTPActionIds(tpid string) ([]string, error) { return ids, nil } +func (self *SQLStorage) ExistsTPActionTimings(tpid, atId 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_ACTION_TIMINGS, tpid, atId)).Scan(&exists) + if err != nil { + return false, err + } + return exists, nil +} + +// Sets actionTimings in sqlDB. Imput is expected in form map[actionTimingId][]rows, eg a full .csv file content +func (self *SQLStorage) SetTPActionTimings(tpid string, ats map[string][]*utils.TPActionTimingsRow) error { + if len(ats) == 0 { + return nil //Nothing to set + } + qry := fmt.Sprintf("INSERT INTO %s (tpid,tag,actions_tag,timing_tag,weight) VALUES ", utils.TBL_TP_ACTION_TIMINGS) + for atId, atRows := range ats { + for idx, atsRow := range atRows { + if idx != 0 { //Consecutive values after the first will be prefixed with "," as separator + qry += "," + } + qry += fmt.Sprintf("('%s','%s','%s','%s',%f)", + tpid, atId, atsRow.ActionsId, atsRow.TimingId, atsRow.Weight) + } + } + if _, err := self.Db.Exec(qry); err != nil { + return err + } + return nil +} + +func (self *SQLStorage) GetTPActionTimings(tpid, atId string) (map[string][]*utils.TPActionTimingsRow, error) { + ats := make(map[string][]*utils.TPActionTimingsRow) + q := fmt.Sprintf("SELECT tag,actions_tag,timing_tag,weight FROM %s WHERE tpid='%s'", utils.TBL_TP_ACTION_TIMINGS, tpid) + if atId != "" { + q += fmt.Sprintf(" AND tag='%s'", atId) + } + rows, err := self.Db.Query(q) + if err != nil { + return nil, err + } + defer rows.Close() + i := 0 + for rows.Next() { + i++ //Keep here a reference so we know we got at least one result + var tag, actionsId, timingId string + var weight float64 + if err = rows.Scan(&tag, &actionsId, &timingId, &weight); err!= nil { + return nil, err + } + ats[tag] = append(ats[tag], &utils.TPActionTimingsRow{actionsId, timingId, weight}) + } + return ats, nil +} + +func (self *SQLStorage) GetTPActionTimingIds(tpid string) ([]string, error) { + rows, err := self.Db.Query(fmt.Sprintf("SELECT DISTINCT tag FROM %s where tpid='%s'", utils.TBL_TP_ACTION_TIMINGS, 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 + if err = rows.Scan(&id); err != nil { + return nil, err + } + ids = append(ids, id) + } + if i == 0 { + return nil, nil + } + return ids, nil +} + +func (self *SQLStorage) GetUserBalance(string) (ub *UserBalance, err error) { return } + +func (self *SQLStorage) SetUserBalance(ub *UserBalance) (err error) { return } + func (self *SQLStorage) GetActions(string) (as Actions, err error) { return } func (self *SQLStorage) SetActions(key string, as Actions) (err error) { return } -func (self *SQLStorage) GetUserBalance(string) (ub *UserBalance, err error) { return } - -func (self *SQLStorage) SetUserBalance(ub *UserBalance) (err error) { return } - func (self *SQLStorage) GetActionTimings(key string) (ats ActionTimings, err error) { return } func (self *SQLStorage) SetActionTimings(key string, ats ActionTimings) (err error) { return } @@ -978,7 +1054,7 @@ func (self *SQLStorage) GetTpActionTriggers(tpid, tag string) (map[string][]*Act var id int var threshold, weight float64 var tpid, tag, balances_tag, direction, destinations_tag, actions_tag, thresholdType string - if err := rows.Scan(&id, &tpid, &tag, &balances_tag, &direction, &threshold, &thresholdType, &destinations_tag, &actions_tag, &weight); err != nil { + if err := rows.Scan(&id, &tpid, &tag, &balances_tag, &direction, &thresholdType, &threshold, &destinations_tag, &actions_tag, &weight); err != nil { return nil, err } @@ -986,8 +1062,8 @@ func (self *SQLStorage) GetTpActionTriggers(tpid, tag string) (map[string][]*Act Id: utils.GenUUID(), BalanceId: balances_tag, Direction: direction, - ThresholdValue: threshold, ThresholdType: thresholdType, + ThresholdValue: threshold, DestinationId: destinations_tag, ActionsId: actions_tag, Weight: weight, diff --git a/utils/apitpdata.go b/utils/apitpdata.go new file mode 100644 index 000000000..c946aafe2 --- /dev/null +++ b/utils/apitpdata.go @@ -0,0 +1,137 @@ +/* +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 utils + +// This file deals with tp_* data definition + +type TPRate struct { + TPid string // Tariff plan id + RateId string // Rate id + RateSlots []RateSlot // One or more RateSlots +} + +type RateSlot struct { + ConnectFee float64 // ConnectFee applied once the call is answered + Rate float64 // Rate applied + RatedUnits int // Number of billing units this rate applies to + RateIncrements int // This rate will apply in increments of duration + RoundingMethod string // Use this method to round the cost + RoundingDecimals int // Round the cost number of decimals + 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 +} + +type TPDestRateTiming struct { + TPid string // Tariff plan id + DestRateTimingId string // DestinationRate profile id + DestRateTimings []DestRateTiming // Set of destinationid-rateid bindings +} + +type DestRateTiming struct { + DestRatesId string // The DestinationRate identity + TimingId string // The timing identity + Weight float64 // Binding priority taken into consideration when more DestinationRates are active on a time slot +} + +type TPRateProfile struct { + TPid string // Tariff plan id + RateProfileId string // RateProfile id + Tenant string // Tenant's Id + TOR string // TypeOfRecord + Direction string // Traffic direction, OUT is the only one supported for now + Subject string // Rating subject, usually the same as account + RatesFallbackSubject string // Fallback on this subject if rates not found for destination + RatingActivations []RatingActivation // Activate rate profiles at specific time +} + +type RatingActivation struct { + ActivationTime int64 // Time when this profile will become active, defined as unix epoch time + DestRateTimingId string // Id of DestRateTiming profile +} + +type AttrTPRateProfileIds struct { + TPid string // Tariff plan id + Tenant string // Tenant's Id + TOR string // TypeOfRecord + Direction string // Traffic direction + Subject string // Rating subject, usually the same as account +} + +type TPActions struct { + TPid string // Tariff plan id + ActionsId string // Actions id + Actions []Action // Set of actions this Actions profile will perform +} + +type Action struct { + Identifier string // Identifier mapped in the code + BalanceId string // Type of balance the action will operate on + Direction string // Balance direction + Units float64 // Number of units to add/deduct + ExpirationTime int64 // Time when the units will expire + DestinationId string // Destination profile id + RateType string // Type of price + Rate float64 // Price value + MinutesWeight float64 // Minutes weight + Weight float64 // Action's weight +} + +type ApiTPActionTimings struct { + TPid string // Tariff plan id + ActionTimingsId string // ActionTimings id + ActionTimings []ApiActionTiming // Set of ActionTiming bindings this profile will group +} + +type ApiActionTiming struct { + ActionsId string // Actions id + TimingId string // Timing profile id + Weight float64 // Binding's weight +} + +type TPActionTriggers struct { + TPid string // Tariff plan id + ActionTriggerId string // ActionTrigger id + BalanceId string // Id of the balance this trigger monitors + Direction string // Traffic direction + ThresholdType string // This threshold type + ThresholdValue float64 // Threshold + DestinationId string // Id of the destination profile + ActionsId string // Actions which will execute on threshold reached + Weight float64 // weight +} + +type TPAccountActions struct { + TPid string // Tariff plan id + AccountActionsId string // AccountActions id + Tenant string // Tenant's Id + Account string // Account name + Direction string // Traffic direction + ActionTimingsId string // Id of ActionTimings profile to use + ActionTriggersId string // Id of ActionTriggers profile to use +} diff --git a/utils/tpdata.go b/utils/tpdata.go index d622886cf..3b1fe33e8 100644 --- a/utils/tpdata.go +++ b/utils/tpdata.go @@ -18,120 +18,11 @@ along with this program. If not, see package utils -// This file deals with tp_* data definition -type TPRate struct { - TPid string // Tariff plan id - RateId string // Rate id - RateSlots []RateSlot // One or more RateSlots -} - -type RateSlot struct { - ConnectFee float64 // ConnectFee applied once the call is answered - Rate float64 // Rate applied - RatedUnits int // Number of billing units this rate applies to - RateIncrements int // This rate will apply in increments of duration - RoundingMethod string // Use this method to round the cost - RoundingDecimals int // Round the cost number of decimals - 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 -} - -type TPDestRateTiming struct { - TPid string // Tariff plan id - DestRateTimingId string // DestinationRate profile id - DestRateTimings []DestRateTiming // Set of destinationid-rateid bindings -} - -type DestRateTiming struct { - DestRatesId string // The DestinationRate identity - TimingId string // The timing identity - Weight float64 // Binding priority taken into consideration when more DestinationRates are active on a time slot -} - -type TPRateProfile struct { - TPid string // Tariff plan id - RateProfileId string // RateProfile id - Tenant string // Tenant's Id - TOR string // TypeOfRecord - Direction string // Traffic direction, OUT is the only one supported for now - Subject string // Rating subject, usually the same as account - RatesFallbackSubject string // Fallback on this subject if rates not found for destination - RatingActivations []RatingActivation // Activate rate profiles at specific time -} - -type RatingActivation struct { - ActivationTime int64 // Time when this profile will become active, defined as unix epoch time - DestRateTimingId string // Id of DestRateTiming profile -} - -type AttrTPRateProfileIds struct { - TPid string // Tariff plan id - Tenant string // Tenant's Id - TOR string // TypeOfRecord - Direction string // Traffic direction - Subject string // Rating subject, usually the same as account -} - -type TPActions struct { - TPid string // Tariff plan id - ActionsId string // Actions id - Actions []Action // Set of actions this Actions profile will perform -} - -type Action struct { - Identifier string // Identifier mapped in the code - BalanceId string // Type of balance the action will operate on - Direction string // Balance direction - Units float64 // Number of units to add/deduct - ExpirationTime int64 // Time when the units will expire - DestinationId string // Destination profile id - RateType string // Type of price - Rate float64 // Price value - MinutesWeight float64 // Minutes weight - Weight float64 // Action's weight -} - -type TPActionTimings struct { - TPid string // Tariff plan id - ActionTimingsId string // ActionTimings id - ActionTimings []ActionTiming // Set of ActionTiming bindings this profile will group -} - -type ActionTiming struct { +// Represents a single row in .csv or storDb, id will be used as key in the map holding all rows +type TPActionTimingsRow struct { ActionsId string // Actions id TimingId string // Timing profile id Weight float64 // Binding's weight } -type TPActionTriggers struct { - TPid string // Tariff plan id - ActionTriggerId string // ActionTrigger id - BalanceId string // Id of the balance this trigger monitors - Direction string // Traffic direction - ThresholdType string // This threshold type - ThresholdValue float64 // Threshold - DestinationId string // Id of the destination profile - ActionsId string // Actions which will execute on threshold reached - Weight float64 // weight -} - -type TPAccountActions struct { - TPid string // Tariff plan id - AccountActionsId string // AccountActions id - Tenant string // Tenant's Id - Account string // Account name - Direction string // Traffic direction - ActionTimingsId string // Id of ActionTimings profile to use - ActionTriggersId string // Id of ActionTriggers profile to use -}