diff --git a/apier/apier.go b/apier/apier.go
index 6915ba96e..de1fd3578 100644
--- a/apier/apier.go
+++ b/apier/apier.go
@@ -19,10 +19,245 @@ along with this program. If not, see
package apier
import (
+ "errors"
+ "fmt"
"github.com/cgrates/cgrates/rater"
+ "github.com/cgrates/cgrates/scheduler"
+ "github.com/cgrates/cgrates/utils"
+ "log"
)
type Apier struct {
StorDb rater.DataStorage
Getter rater.DataStorage
+ Sched *scheduler.Scheduler
+}
+
+type AttrDestination struct {
+ Id string
+ Prefixes []string
+}
+
+func (self *Apier) GetDestination(attr *AttrDestination, reply *AttrDestination) error {
+ if dst, err := self.Getter.GetDestination(attr.Id); err != nil {
+ return errors.New(utils.ERR_NOT_FOUND)
+ } else {
+ reply.Id = dst.Id
+ reply.Prefixes = dst.Prefixes
+ }
+ return nil
+}
+
+func (self *Apier) SetDestination(attr *AttrDestination, reply *float64) error {
+ d := &rater.Destination{
+ Id: attr.Id,
+ Prefixes: attr.Prefixes,
+ }
+ if err := self.Getter.SetDestination(d); err != nil {
+ return err
+ }
+ return nil
+}
+
+type AttrGetBalance struct {
+ Tenant string
+ Account string
+ BalanceId string
+ Direction string
+}
+
+// Get balance
+func (self *Apier) GetBalance(attr *AttrGetBalance, reply *float64) error {
+ tag := fmt.Sprintf("%s:%s:%s", attr.Direction, attr.Tenant, attr.Account)
+ userBalance, err := self.Getter.GetUserBalance(tag)
+ if err != nil {
+ return err
+ }
+
+ if attr.Direction == "" {
+ attr.Direction = rater.OUTBOUND
+ }
+
+ if balance, balExists := userBalance.BalanceMap[attr.BalanceId+attr.Direction]; !balExists {
+ *reply = 0.0
+ } else {
+ *reply = balance
+ }
+ return nil
+}
+
+type AttrAddBalance struct {
+ Tenant string
+ Account string
+ BalanceId string
+ Direction string
+ Value float64
+}
+
+func (self *Apier) AddBalance(attr *AttrAddBalance, reply *float64) error {
+ // what storage instance do we use?
+ tag := fmt.Sprintf("%s:%s:%s", attr.Direction, attr.Tenant, attr.Account)
+
+ if _, err := self.Getter.GetUserBalance(tag); err != nil {
+ // create user balance if not exists
+ ub := &rater.UserBalance{
+ Id: tag,
+ }
+ if err := self.Getter.SetUserBalance(ub); err != nil {
+ return err
+ }
+ }
+
+ at := &rater.ActionTiming{
+ UserBalanceIds: []string{tag},
+ }
+
+ if attr.Direction == "" {
+ attr.Direction = rater.OUTBOUND
+ }
+
+ at.SetActions(rater.Actions{&rater.Action{ActionType: rater.TOPUP, BalanceId: attr.BalanceId, Direction: attr.Direction, Units: attr.Value}})
+
+ if err := at.Execute(); err != nil {
+ return err
+ }
+ // what to put in replay?
+ return nil
+}
+
+type AttrExecuteAction struct {
+ Direction string
+ Tenant string
+ Account string
+ BalanceId string
+ ActionsId string
+}
+
+func (self *Apier) ExecuteAction(attr *AttrExecuteAction, reply *float64) error {
+ tag := fmt.Sprintf("%s:%s:%s", attr.Direction, attr.Tenant, attr.Account)
+ at := &rater.ActionTiming{
+ UserBalanceIds: []string{tag},
+ ActionsId: attr.ActionsId,
+ }
+
+ if err := at.Execute(); err != nil {
+ return err
+ }
+ // what to put in replay
+ return nil
+}
+
+type AttrSetRatingProfile struct {
+ Direction string
+ Tenant string
+ TOR string
+ Subject string
+ SourceSubject string
+}
+
+func (self *Apier) SetRatingProfile(attr *AttrSetRatingProfile, reply *float64) error {
+ cd := &rater.CallDescriptor{
+ Direction: attr.Direction,
+ Tenant: attr.Tenant,
+ TOR: attr.TOR,
+ Subject: attr.Subject,
+ }
+ destination, err := self.Getter.GetRatingProfile(cd.GetKey())
+ log.Print("1: " + cd.GetKey())
+ if err != nil {
+ destination = &rater.RatingProfile{Id: cd.GetKey()}
+ }
+ cd.Subject = attr.SourceSubject
+ source, err := self.Getter.GetRatingProfile(cd.GetKey())
+ log.Print("2: " + cd.GetKey())
+ if err != nil {
+ log.Print("here: " + err.Error())
+ return err
+ }
+ destination.DestinationMap = source.DestinationMap
+ destination.FallbackKey = source.FallbackKey
+ err = self.Getter.SetRatingProfile(destination)
+ return err
+}
+
+type AttrActionTrigger struct {
+ Tenant string
+ Account string
+ Direction string
+ BalanceId string
+ ThresholdValue float64
+ DestinationId string
+ Weight float64
+ ActionsId string
+}
+
+func (self *Apier) AddTriggeredAction(attr *AttrActionTrigger, reply *float64) error {
+ if attr.Direction == "" {
+ attr.Direction = rater.OUTBOUND
+ }
+
+ at := &rater.ActionTrigger{
+ Id: utils.GenUUID(),
+ BalanceId: attr.BalanceId,
+ Direction: attr.Direction,
+ ThresholdValue: attr.ThresholdValue,
+ DestinationId: attr.DestinationId,
+ Weight: attr.Weight,
+ ActionsId: attr.ActionsId,
+ Executed: false,
+ }
+
+ tag := fmt.Sprintf("%s:%s:%s", attr.Direction, attr.Tenant, attr.Account)
+ var dbErr error
+ rater.AccLock.Guard(tag, func() (float64, error) {
+ userBalance, err := self.Getter.GetUserBalance(tag)
+ if err != nil {
+ dbErr = err
+ return 0, err
+ }
+
+ userBalance.ActionTriggers = append(userBalance.ActionTriggers, at)
+
+ if err = self.Getter.SetUserBalance(userBalance); err != nil {
+ dbErr = err
+ return 0, err
+ }
+ return 0, nil
+ })
+
+ return dbErr
+}
+
+type AttrAccount struct {
+ Tenant string
+ Direction string
+ Account string
+ Type string // prepaid-postpaid
+ ActionTimingsId string
+}
+
+func (self *Apier) AddAccount(attr *AttrAccount, reply *float64) error {
+ tag := fmt.Sprintf("%s:%s:%s", attr.Direction, attr.Tenant, attr.Account)
+ ub := &rater.UserBalance{
+ Id: tag,
+ Type: attr.Type,
+ }
+ if err := self.Getter.SetUserBalance(ub); err != nil {
+ return err
+ }
+ if attr.ActionTimingsId != "" {
+ if ats, err := self.Getter.GetActionTimings(attr.ActionTimingsId); err == nil {
+ for _, at := range ats {
+ at.UserBalanceIds = append(at.UserBalanceIds, tag)
+ }
+ self.Getter.SetActionTimings(attr.ActionTimingsId, ats)
+ if self.Sched != nil {
+ self.Sched.LoadActionTimings(self.Getter)
+ self.Sched.Restart()
+ }
+ } else {
+ return err
+ }
+ }
+ return nil
}
diff --git a/apier/tpdestinations.go b/apier/tpdestinations.go
index 0fb9e310d..16ef2d22f 100644
--- a/apier/tpdestinations.go
+++ b/apier/tpdestinations.go
@@ -21,13 +21,13 @@ package apier
import (
"errors"
"fmt"
- "github.com/cgrates/cgrates/rater"
+ "github.com/cgrates/cgrates/rater"
"github.com/cgrates/cgrates/utils"
)
type AttrGetTPDestinations struct {
TPid string
- DestinationsTag string
+ DestinationsTag string
}
// Return destinations profile for a destination tag received as parameter
@@ -44,184 +44,3 @@ func (self *Apier) GetTPDestinations(attrs AttrGetTPDestinations, reply *rater.D
}
return nil
}
-
-type AttrDestination struct {
- Id string
- Prefixes []string
-}
-
-func (self *Apier) GetDestination(attr *AttrDestination, reply *AttrDestination) error {
- if dst, err := self.Getter.GetDestination(attr.Id); err != nil {
- return errors.New(utils.ERR_NOT_FOUND)
- } else {
- reply.Id = dst.Id
- reply.Prefixes = dst.Prefixes
- }
- return nil
-}
-
-func (self *Apier) SetDestination(attr *AttrDestination, reply *float64) error {
- d := &rater.Destination{
- Id: attr.Id,
- Prefixes: attr.Prefixes,
- }
- if err := self.Getter.SetDestination(d); err != nil {
- return err
- }
- return nil
-}
-
-type AttrGetBalance struct {
- Tenant string
- Account string
- BalanceId string
- Direction string
-}
-
-// Get balance
-func (self *Apier) GetBalance(attr *AttrGetBalance, reply *float64) error {
- tag := fmt.Sprintf("%s:%s:%s", attr.Direction, attr.Tenant, attr.Account)
- userBalance, err := self.Getter.GetUserBalance(tag)
- if err != nil {
- return err
- }
-
- if attr.Direction == "" {
- attr.Direction = rater.OUTBOUND
- }
-
- if balance, balExists := userBalance.BalanceMap[attr.BalanceId+attr.Direction]; !balExists {
- // No match, balanceId not found
- return errors.New(utils.ERR_NOT_FOUND)
- } else {
- *reply = balance
- }
- return nil
-}
-
-type AttrAddBalance struct {
- Tenant string
- Account string
- BalanceId string
- Direction string
- Value float64
-}
-
-func (self *Apier) AddBalance(attr *AttrAddBalance, reply *float64) error {
- // what storage instance do we use?
- tag := fmt.Sprintf("%s:%s:%s", attr.Direction, attr.Tenant, attr.Account)
-
- at := &rater.ActionTiming{
- UserBalanceIds: []string{tag},
- }
-
- if attr.Direction == "" {
- attr.Direction = rater.OUTBOUND
- }
-
- at.SetActions(rater.Actions{&rater.Action{BalanceId: attr.BalanceId, Direction: attr.Direction, Units: attr.Value}})
-
- if err := at.Execute(); err != nil {
- return err
- }
- // what to put in replay?
- return nil
-}
-
-type AttrExecuteAction struct {
- Direction string
- Tenant string
- Account string
- BalanceId string
- ActionsId string
-}
-
-func (self *Apier) ExecuteAction(attr *AttrExecuteAction, reply *float64) error {
- tag := fmt.Sprintf("%s:%s:%s", attr.Direction, attr.Tenant, attr.Account)
- at := &rater.ActionTiming{
- UserBalanceIds: []string{tag},
- ActionsId: attr.ActionsId,
- }
-
- if err := at.Execute(); err != nil {
- return err
- }
- // what to put in replay
- return nil
-}
-
-type AttrSetRatingProfile struct {
- Direction string
- Tenant string
- TOR string
- Subject string
- RatingProfileId string
-}
-
-func (self *Apier) SetRatingProfile(attr *AttrSetRatingProfile, reply *float64) error {
- cd := &rater.CallDescriptor{
- Direction: attr.Direction,
- Tenant: attr.Tenant,
- TOR: attr.TOR,
- Subject: attr.Subject,
- }
- subject, err := self.Getter.GetRatingProfile(cd.GetKey())
- if err != nil {
- return err
- }
- rp, err := self.Getter.GetRatingProfile(attr.RatingProfileId)
- if err != nil {
- return err
- }
- subject.DestinationMap = rp.DestinationMap
- err = self.Getter.SetRatingProfile(subject)
- return err
-}
-
-type AttrActionTrigger struct {
- Tenant string
- Account string
- Direction string
- BalanceId string
- ThresholdValue float64
- DestinationId string
- Weight float64
- ActionsId string
-}
-
-func (self *Apier) AddTriggeredAction(attr *AttrActionTrigger, reply *float64) error {
- if attr.Direction == "" {
- attr.Direction = rater.OUTBOUND
- }
-
- at := &rater.ActionTrigger{
- Id: utils.GenUUID(),
- BalanceId: attr.BalanceId,
- Direction: attr.Direction,
- ThresholdValue: attr.ThresholdValue,
- DestinationId: attr.DestinationId,
- Weight: attr.Weight,
- ActionsId: attr.ActionsId,
- Executed: false,
- }
-
- tag := fmt.Sprintf("%s:%s:%s", attr.Direction, attr.Tenant, attr.Account)
- var dbErr error
- rater.AccLock.Guard(tag, func() (float64, error) {
- userBalance, err := self.Getter.GetUserBalance(tag)
- if err != nil {
- dbErr = err
- return 0, err
- }
-
- userBalance.ActionTriggers = append(userBalance.ActionTriggers, at)
-
- if err = self.Getter.SetUserBalance(userBalance); err != nil {
- dbErr = err
- return 0, err
- }
- return 0, nil
- })
-
- return dbErr
-}
diff --git a/cmd/cgr-rater/cgr-rater.go b/cmd/cgr-rater/cgr-rater.go
index b2b618a7c..30f1f8e7a 100644
--- a/cmd/cgr-rater/cgr-rater.go
+++ b/cmd/cgr-rater/cgr-rater.go
@@ -64,7 +64,7 @@ var (
err error
)
-func listenToRPCRequests(rpcResponder interface{}, rpcAddress string, rpc_encoding string, getter rater.DataStorage, loggerDb rater.DataStorage) {
+func listenToRPCRequests(rpcResponder interface{}, apier *apier.Apier, rpcAddress string, rpc_encoding string, getter rater.DataStorage, loggerDb rater.DataStorage) {
l, err := net.Listen("tcp", rpcAddress)
if err != nil {
rater.Logger.Crit(fmt.Sprintf(" Could not listen to %v: %v", rpcAddress, err))
@@ -75,7 +75,7 @@ func listenToRPCRequests(rpcResponder interface{}, rpcAddress string, rpc_encodi
rater.Logger.Info(fmt.Sprintf(" Listening for incomming RPC requests on %v", l.Addr()))
rpc.Register(rpcResponder)
- rpc.Register(&apier.Apier{StorDb: loggerDb, Getter: getter})
+ rpc.Register(apier)
var serveFunc func(io.ReadWriteCloser)
if rpc_encoding == JSON {
serveFunc = jsonrpc.ServeConn
@@ -296,15 +296,16 @@ func main() {
go stopRaterSingnalHandler()
}
responder := &rater.Responder{ExitChan: exitChan}
+ apier := &apier.Apier{StorDb: loggerDb, Getter: getter}
if cfg.RaterEnabled && !cfg.BalancerEnabled && cfg.RaterListen != INTERNAL {
rater.Logger.Info(fmt.Sprintf("Starting CGRateS Rater on %s.", cfg.RaterListen))
- go listenToRPCRequests(responder, cfg.RaterListen, cfg.RPCEncoding, getter, loggerDb)
+ go listenToRPCRequests(responder, apier, cfg.RaterListen, cfg.RPCEncoding, getter, loggerDb)
}
if cfg.BalancerEnabled {
rater.Logger.Info(fmt.Sprintf("Starting CGRateS Balancer on %s.", cfg.BalancerListen))
go stopBalancerSingnalHandler()
responder.Bal = bal
- go listenToRPCRequests(responder, cfg.BalancerListen, cfg.RPCEncoding, getter, loggerDb)
+ go listenToRPCRequests(responder, apier, cfg.BalancerListen, cfg.RPCEncoding, getter, loggerDb)
if cfg.RaterEnabled {
rater.Logger.Info("Starting internal rater.")
bal.AddClient("local", new(rater.ResponderWorker))
@@ -316,6 +317,7 @@ func main() {
go func() {
sched := scheduler.NewScheduler()
go reloadSchedulerSingnalHandler(sched, getter)
+ apier.Sched = sched
sched.LoadActionTimings(getter)
sched.Loop()
}()
diff --git a/docs/apicalls.rst b/docs/apicalls.rst
index d9070d1e7..adfb4ae6e 100644
--- a/docs/apicalls.rst
+++ b/docs/apicalls.rst
@@ -428,6 +428,22 @@ AddTriggeredAction
Example
AddTriggeredAction(attr \*AttrActionTrigger, reply \*float64)
+AddAcount
++++++++++
+
+::
+
+ type AttrAccount struct {
+ Tenant string
+ Direction string
+ Account string
+ Type string // prepaid-postpaid
+ ActionTimingsId string
+ }
+
+Example
+ AddAccount(attr \*AttrAccount, reply \*float64)
+
SetRatingProfile
++++++++++++++++
@@ -440,7 +456,7 @@ Sets the rating profile for the specified subject.
Tenant string
TOR string
Subject string
- RatingProfileId string
+ SourceSubject string
}
Example
diff --git a/rater/action.go b/rater/action.go
index c28c41885..d9c317d97 100644
--- a/rater/action.go
+++ b/rater/action.go
@@ -36,31 +36,45 @@ type Action struct {
MinuteBucket *MinuteBucket
}
+const (
+ LOG = "LOG"
+ RESET_TRIGGERS = "RESET_TRIGGERS"
+ SET_POSTPAID = "SET_POSTPAID"
+ RESET_POSTPAID = "RESET_POSTPAID"
+ SET_PREPAID = "SET_PREPAID"
+ RESET_PREPAID = "RESET_PREPAID"
+ TOPUP_RESET = "TOPUP_RESET"
+ TOPUP = "TOPUP"
+ DEBIT = "DEBIT"
+ RESET_COUNTER = "RESET_COUNTER"
+ RESET_COUNTERS = "RESET_COUNTERS"
+)
+
type actionTypeFunc func(*UserBalance, *Action) error
func getActionFunc(typ string) (actionTypeFunc, bool) {
switch typ {
- case "LOG":
+ case LOG:
return logAction, true
- case "RESET_TRIGGERS":
+ case RESET_TRIGGERS:
return resetTriggersAction, true
- case "SET_POSTPAID":
+ case SET_POSTPAID:
return setPostpaidAction, true
- case "RESET_POSTPAID":
+ case RESET_POSTPAID:
return resetPostpaidAction, true
- case "SET_PREPAID":
+ case SET_PREPAID:
return setPrepaidAction, true
- case "RESET_PREPAID":
+ case RESET_PREPAID:
return resetPrepaidAction, true
- case "TOPUP_RESET":
+ case TOPUP_RESET:
return topupResetAction, true
- case "TOPUP":
+ case TOPUP:
return topupAction, true
- case "DEBIT":
+ case DEBIT:
return debitAction, true
- case "RESET_COUNTER":
+ case RESET_COUNTER:
return resetCounterAction, true
- case "RESET_COUNTERS":
+ case RESET_COUNTERS:
return resetCountersAction, true
}
return nil, false