mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
Implement APIerSv1.TransferBalance API
This commit is contained in:
committed by
Dan Christian Bogos
parent
41976e5721
commit
0c32f1761c
@@ -19,7 +19,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
package v1
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"slices"
|
||||
"strings"
|
||||
@@ -712,6 +714,92 @@ func (apierSv1 *APIerSv1) RemoveBalances(ctx *context.Context, attr *utils.AttrS
|
||||
return nil
|
||||
}
|
||||
|
||||
// TransferBalance sets a temporary *transfer_balance action, which will be executed immediately.
|
||||
func (apierSv1 *APIerSv1) TransferBalance(ctx *context.Context, attr utils.AttrTransferBalance, reply *string) (err error) {
|
||||
if missing := utils.MissingStructFields(&attr, []string{
|
||||
utils.SrcAccountID, utils.SrcBalanceID,
|
||||
utils.DestAccountID, utils.DestBalanceID,
|
||||
utils.Units, utils.BalanceType}); len(missing) != 0 {
|
||||
return utils.NewErrMandatoryIeMissing(missing...)
|
||||
}
|
||||
if attr.Tenant == "" {
|
||||
attr.Tenant = apierSv1.Config.GeneralCfg().DefaultTenant
|
||||
}
|
||||
|
||||
// Set *transfer_balance action.
|
||||
actionID := fmt.Sprintf("tmp_act_%s", utils.UUIDSha1Prefix())
|
||||
if !attr.Overwrite {
|
||||
var exists bool
|
||||
if exists, err = apierSv1.DataManager.HasData(utils.ActionPrefix, actionID, ""); err != nil {
|
||||
return utils.NewErrServerError(err)
|
||||
} else if exists {
|
||||
return utils.ErrExists
|
||||
}
|
||||
}
|
||||
var extraParams []byte
|
||||
extraParams, err = json.Marshal(map[string]string{
|
||||
utils.DestAccountID: attr.Tenant + ":" + attr.DestAccountID,
|
||||
utils.DestBalanceID: attr.DestBalanceID,
|
||||
})
|
||||
if err != nil {
|
||||
return utils.NewErrServerError(err)
|
||||
}
|
||||
action := &engine.Action{
|
||||
Id: actionID,
|
||||
ActionType: utils.MetaTransferBalance,
|
||||
ExtraParameters: string(extraParams),
|
||||
Balance: &engine.BalanceFilter{
|
||||
ID: utils.StringPointer(attr.SrcBalanceID),
|
||||
Type: utils.StringPointer(attr.BalanceType),
|
||||
Value: &utils.ValueFormula{Static: attr.Units},
|
||||
},
|
||||
}
|
||||
if err = apierSv1.DataManager.SetActions(actionID, engine.Actions{action}); err != nil {
|
||||
return utils.NewErrServerError(err)
|
||||
}
|
||||
|
||||
// Remove the action.
|
||||
defer func() {
|
||||
if removeErr := apierSv1.DataManager.RemoveActions(actionID); err != nil {
|
||||
err = errors.Join(err, removeErr)
|
||||
return
|
||||
}
|
||||
if reloadCacheErr := apierSv1.ConnMgr.Call(context.TODO(), apierSv1.Config.ApierCfg().CachesConns,
|
||||
utils.CacheSv1ReloadCache, &utils.AttrReloadCacheWithAPIOpts{
|
||||
ActionIDs: []string{actionID},
|
||||
}, reply); reloadCacheErr != nil {
|
||||
err = errors.Join(err, reloadCacheErr)
|
||||
return
|
||||
}
|
||||
if setLoadIDErr := apierSv1.DataManager.SetLoadIDs(map[string]int64{utils.CacheActions: time.Now().UnixNano()}); setLoadIDErr != nil {
|
||||
err = errors.Join(err, setLoadIDErr)
|
||||
return
|
||||
}
|
||||
*reply = utils.OK
|
||||
}()
|
||||
|
||||
if err = apierSv1.ConnMgr.Call(context.TODO(), apierSv1.Config.ApierCfg().CachesConns,
|
||||
utils.CacheSv1ReloadCache, &utils.AttrReloadCacheWithAPIOpts{
|
||||
ActionIDs: []string{actionID},
|
||||
}, reply); err != nil {
|
||||
return utils.NewErrServerError(err)
|
||||
}
|
||||
//generate a loadID for CacheActions and store it in database
|
||||
if err = apierSv1.DataManager.SetLoadIDs(map[string]int64{utils.CacheActions: time.Now().UnixNano()}); err != nil {
|
||||
return utils.NewErrServerError(err)
|
||||
}
|
||||
|
||||
// Execute the action.
|
||||
at := &engine.ActionTiming{
|
||||
ActionsID: actionID,
|
||||
}
|
||||
at.SetAccountIDs(utils.StringMap{utils.ConcatenatedKey(attr.Tenant, attr.SrcAccountID): true})
|
||||
if err = at.Execute(apierSv1.FilterS); err != nil {
|
||||
return utils.NewErrServerError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (apierSv1 *APIerSv1) GetAccountsCount(ctx *context.Context, attr *utils.TenantWithAPIOpts, reply *int) (err error) {
|
||||
tnt := attr.Tenant
|
||||
if tnt == utils.EmptyString {
|
||||
|
||||
@@ -21,7 +21,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
package general_tests
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
@@ -81,13 +80,10 @@ ACT_TOPUP_DEST,*topup_reset,,,balance_dest,*monetary,,*any,,,*unlimited,,10,10,f
|
||||
ACT_TRANSFER,*transfer_balance,"{""DestAccountID"":""cgrates.org:ACC_DEST"",""DestBalanceID"":""balance_dest""}",,balance_src,*monetary,,,,,*unlimited,,4,,,,`,
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
testEnv := TestEnvironment{
|
||||
Name: "TestTransferBalance",
|
||||
// Encoding: *encoding,
|
||||
Name: "TestTransferBalance",
|
||||
ConfigJSON: content,
|
||||
TpFiles: tpFiles,
|
||||
LogBuffer: buf,
|
||||
}
|
||||
client, _, shutdown, err := testEnv.Setup(t, *waitRater)
|
||||
if err != nil {
|
||||
@@ -134,7 +130,7 @@ ACT_TRANSFER,*transfer_balance,"{""DestAccountID"":""cgrates.org:ACC_DEST"",""De
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("CheckFinalBalances", func(t *testing.T) {
|
||||
t.Run("CheckBalancesAfterActionExecute", func(t *testing.T) {
|
||||
var acnts []*engine.Account
|
||||
if err := client.Call(context.Background(), utils.APIerSv2GetAccounts,
|
||||
&utils.AttrGetAccounts{
|
||||
@@ -163,4 +159,49 @@ ACT_TRANSFER,*transfer_balance,"{""DestAccountID"":""cgrates.org:ACC_DEST"",""De
|
||||
t.Errorf("received account with unexpected balance: %v", balance)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("TransferBalanceByAPI", func(t *testing.T) {
|
||||
var reply string
|
||||
if err := client.Call(context.Background(), utils.APIerSv1TransferBalance, utils.AttrTransferBalance{
|
||||
Tenant: "cgrates.org",
|
||||
SrcAccountID: "ACC_SRC",
|
||||
SrcBalanceID: "balance_src",
|
||||
DestAccountID: "ACC_DEST",
|
||||
DestBalanceID: "balance_dest",
|
||||
Units: 2,
|
||||
BalanceType: utils.MetaMonetary,
|
||||
}, &reply); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("CheckBalancesAfterTransferBalanceAPI", func(t *testing.T) {
|
||||
var acnts []*engine.Account
|
||||
if err := client.Call(context.Background(), utils.APIerSv2GetAccounts,
|
||||
&utils.AttrGetAccounts{
|
||||
Tenant: "cgrates.org",
|
||||
}, &acnts); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if len(acnts) != 2 {
|
||||
t.Fatal("expecting 2 accounts to be retrieved")
|
||||
}
|
||||
sort.Slice(acnts, func(i, j int) bool {
|
||||
return acnts[i].ID > acnts[j].ID
|
||||
})
|
||||
if len(acnts[0].BalanceMap) != 1 || len(acnts[0].BalanceMap[utils.MetaMonetary]) != 1 {
|
||||
t.Errorf("expected account to have only one balance of type *monetary, received %v", acnts[0])
|
||||
}
|
||||
balance := acnts[0].BalanceMap[utils.MetaMonetary][0]
|
||||
if balance.ID != "balance_src" || balance.Value != 4 {
|
||||
t.Errorf("received account with unexpected balance: %v", balance)
|
||||
}
|
||||
if len(acnts[1].BalanceMap) != 1 || len(acnts[1].BalanceMap[utils.MetaMonetary]) != 1 {
|
||||
t.Errorf("expected account to have only one balance of type *monetary, received %v", acnts[1])
|
||||
}
|
||||
balance = acnts[1].BalanceMap[utils.MetaMonetary][0]
|
||||
if balance.ID != "balance_dest" || balance.Value != 16 {
|
||||
t.Errorf("received account with unexpected balance: %v", balance)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -879,6 +879,18 @@ type AttrBalance struct {
|
||||
Cdrlog bool
|
||||
}
|
||||
|
||||
type AttrTransferBalance struct {
|
||||
Tenant string
|
||||
SrcAccountID string
|
||||
SrcBalanceID string
|
||||
DestAccountID string
|
||||
DestBalanceID string
|
||||
Units float64
|
||||
BalanceType string
|
||||
Overwrite bool
|
||||
Cdrlog bool
|
||||
}
|
||||
|
||||
// TPResourceProfile is used in APIs to manage remotely offline ResourceProfile
|
||||
type TPResourceProfile struct {
|
||||
TPid string
|
||||
|
||||
@@ -491,6 +491,10 @@ const (
|
||||
EventSource = "EventSource"
|
||||
AccountID = "AccountID"
|
||||
AccountIDs = "AccountIDs"
|
||||
SrcAccountID = "SrcAccountID"
|
||||
DestAccountID = "DestAccountID"
|
||||
SrcBalanceID = "SrcBalanceID"
|
||||
DestBalanceID = "DestBalanceID"
|
||||
ResourceID = "ResourceID"
|
||||
TotalUsage = "TotalUsage"
|
||||
StatID = "StatID"
|
||||
@@ -1319,6 +1323,7 @@ const (
|
||||
APIerSv1ExportToFolder = "APIerSv1.ExportToFolder"
|
||||
APIerSv1GetCost = "APIerSv1.GetCost"
|
||||
APIerSv1SetBalance = "APIerSv1.SetBalance"
|
||||
APIerSv1TransferBalance = "APIerSv1.TransferBalance"
|
||||
APIerSv1GetFilter = "APIerSv1.GetFilter"
|
||||
APIerSv1GetFilterIndexes = "APIerSv1.GetFilterIndexes"
|
||||
APIerSv1RemoveFilterIndexes = "APIerSv1.RemoveFilterIndexes"
|
||||
|
||||
Reference in New Issue
Block a user