mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
Make RateProfiles storable in MySQL and Postgres
This commit is contained in:
committed by
Dan Christian Bogos
parent
a559563810
commit
da41db3f56
@@ -94,11 +94,11 @@ func TestRateSIT(t *testing.T) {
|
||||
case utils.MetaMongo:
|
||||
ratePrfConfigDIR = "rates_mongo"
|
||||
case utils.MetaRedis:
|
||||
t.SkipNow()
|
||||
ratePrfConfigDIR = "rates_redis"
|
||||
case utils.MetaMySQL:
|
||||
ratePrfConfigDIR = "rates_mysql"
|
||||
case utils.MetaPostgres:
|
||||
t.SkipNow()
|
||||
ratePrfConfigDIR = "rates_postgres"
|
||||
default:
|
||||
t.Fatal("Unknown Database type")
|
||||
}
|
||||
@@ -1598,8 +1598,15 @@ func testRateProfileUpdateRates(t *testing.T) {
|
||||
},
|
||||
}, &result2); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(result2, expectedRate) {
|
||||
t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expectedRate), utils.ToJSON(result2))
|
||||
} else if *utils.DBType != utils.MetaMySQL && *utils.DBType != utils.MetaPostgres {
|
||||
if !reflect.DeepEqual(result2, expectedRate) {
|
||||
t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expectedRate), utils.ToJSON(result2))
|
||||
}
|
||||
} else if *utils.DBType == utils.MetaMySQL || *utils.DBType == utils.MetaPostgres {
|
||||
expectedRate.Rates["RT_THUESDAY"].IntervalRates[0].FixedFee = utils.NewDecimal(2, 1)
|
||||
if !reflect.DeepEqual(result2, expectedRate) {
|
||||
t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expectedRate), utils.ToJSON(result2))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1686,9 +1693,17 @@ func testRateProfileRemoveMultipleRates(t *testing.T) {
|
||||
},
|
||||
}, &result2); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(result2, expectedRate) {
|
||||
t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expectedRate), utils.ToJSON(result2))
|
||||
} else if *utils.DBType != utils.MetaMySQL && *utils.DBType != utils.MetaPostgres {
|
||||
if !reflect.DeepEqual(result2, expectedRate) {
|
||||
t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expectedRate), utils.ToJSON(result2))
|
||||
}
|
||||
} else if *utils.DBType == utils.MetaMySQL || *utils.DBType == utils.MetaPostgres {
|
||||
expectedRate.Rates["RT_THUESDAY"].IntervalRates[0].FixedFee = utils.NewDecimal(2, 1)
|
||||
if !reflect.DeepEqual(result2, expectedRate) {
|
||||
t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expectedRate), utils.ToJSON(result2))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func testRateProfileSetMultipleRatesInProfile(t *testing.T) {
|
||||
@@ -1878,8 +1893,16 @@ func testRateProfileSetMultipleRatesInProfile(t *testing.T) {
|
||||
},
|
||||
}, &result2); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(result2, expectedRate) {
|
||||
t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expectedRate), utils.ToJSON(result2))
|
||||
} else if *utils.DBType != utils.MetaMySQL && *utils.DBType != utils.MetaPostgres {
|
||||
if !reflect.DeepEqual(result2, expectedRate) {
|
||||
t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expectedRate), utils.ToJSON(result2))
|
||||
}
|
||||
} else if *utils.DBType == utils.MetaMySQL || *utils.DBType == utils.MetaPostgres {
|
||||
expectedRate.Rates["RT_THUESDAY"].IntervalRates[0].FixedFee = utils.NewDecimal(2, 1)
|
||||
expectedRate.Rates["RT_SUNDAY"].IntervalRates[1].IntervalStart = utils.NewDecimal(100000000, 0)
|
||||
if !reflect.DeepEqual(result2, expectedRate) {
|
||||
t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expectedRate), utils.ToJSON(result2))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1937,7 +1960,18 @@ func testRateProfileUpdateProfileRatesOverwrite(t *testing.T) {
|
||||
},
|
||||
}, &result2); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(result2, ratePrf.RateProfile) {
|
||||
} else if *utils.DBType != utils.MetaMySQL && *utils.DBType != utils.MetaPostgres {
|
||||
if !reflect.DeepEqual(result2, ratePrf.RateProfile) {
|
||||
t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(ratePrf.RateProfile), utils.ToJSON(result2))
|
||||
}
|
||||
} else if *utils.DBType == utils.MetaMySQL || *utils.DBType == utils.MetaPostgres {
|
||||
ratePrf.RateProfile.MaxCost = utils.NewDecimal(5, 1)
|
||||
if !reflect.DeepEqual(result2, ratePrf.RateProfile) {
|
||||
t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(ratePrf.RateProfile), utils.ToJSON(result2))
|
||||
}
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(result2, ratePrf.RateProfile) {
|
||||
t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(ratePrf.RateProfile), utils.ToJSON(result2))
|
||||
}
|
||||
|
||||
|
||||
@@ -180,10 +180,10 @@ const CGRATES_CFG_JSON = `
|
||||
"*thresholds": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false, "dbConn": "*default"},
|
||||
"*filters": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false, "dbConn": "*default"},
|
||||
"*route_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false, "dbConn": "*default"},
|
||||
"*rate_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false, "dbConn": "*default"},
|
||||
|
||||
// compatible db types: <*internal|*redis|*mongo>
|
||||
"*actions": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false, "dbConn": "*default"},
|
||||
"*rate_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false, "dbConn": "*default"},
|
||||
"*load_ids": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false, "dbConn": "*default"},
|
||||
"*resource_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false, "dbConn": "*default"},
|
||||
"*ip_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false, "dbConn": "*default"},
|
||||
|
||||
@@ -1027,6 +1027,7 @@ func (cfg *CGRConfig) checkConfigSanity() error {
|
||||
// DataDB sanity checks
|
||||
hasOneInternalDB := false // used to reutrn error in case more then 1 internaldb is found
|
||||
allDBsItems := []string{
|
||||
utils.CacheVersions,
|
||||
utils.MetaAccounts,
|
||||
utils.MetaIPProfiles,
|
||||
utils.MetaIPAllocations,
|
||||
@@ -1041,6 +1042,7 @@ func (cfg *CGRConfig) checkConfigSanity() error {
|
||||
utils.MetaThresholds,
|
||||
utils.MetaFilters,
|
||||
utils.MetaRouteProfiles,
|
||||
utils.MetaRateProfiles,
|
||||
}
|
||||
for _, dbcfg := range cfg.dbCfg.DBConns {
|
||||
if dbcfg.Type == utils.MetaInternal {
|
||||
@@ -1080,11 +1082,11 @@ func (cfg *CGRConfig) checkConfigSanity() error {
|
||||
if item == utils.MetaCDRs {
|
||||
if !slices.Contains(storDBTypes, cfg.dbCfg.DBConns[val.DBConn].Type) {
|
||||
return fmt.Errorf("<%s> db item can only be of types <%v>, got <%s>", item,
|
||||
storDBTypes, cfg.dbCfg.DBConns[val.DBConn].Type)
|
||||
storDBTypes[4:], cfg.dbCfg.DBConns[val.DBConn].Type)
|
||||
}
|
||||
} else {
|
||||
if !slices.Contains(dataDBTypes, cfg.dbCfg.DBConns[val.DBConn].Type) {
|
||||
return fmt.Errorf("<%s> db item can only be of types <%v>, got <%s>", item, dataDBTypes, cfg.dbCfg.DBConns[val.DBConn].Type)
|
||||
return fmt.Errorf("<%s> db item can only be of types <%v>, got <%s>", item, dataDBTypes[3:], cfg.dbCfg.DBConns[val.DBConn].Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
"db_conns": {
|
||||
"*default": {
|
||||
"db_type": "*internal",
|
||||
"opts":{
|
||||
"internalDBDumpInterval": "500ms",
|
||||
"internalDBRewriteInterval": "500ms",
|
||||
"internalDBFileSizeLimit": "4k"
|
||||
}
|
||||
"opts":{
|
||||
"internalDBDumpInterval": "500ms",
|
||||
"internalDBRewriteInterval": "500ms",
|
||||
"internalDBFileSizeLimit": "4k"
|
||||
}
|
||||
}
|
||||
},
|
||||
"items":{
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
}
|
||||
},
|
||||
"items": {
|
||||
"*cdrs": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false, "dbConn": "StorDB"}
|
||||
"*cdrs": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false, "dbConn": "StorDB"},
|
||||
"*rate_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false, "dbConn": "StorDB"}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
46
data/conf/samples/rates_postgres/cgrates.json
Normal file
46
data/conf/samples/rates_postgres/cgrates.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
|
||||
"general": {
|
||||
"node_id": "id",
|
||||
},
|
||||
|
||||
"logger": {
|
||||
"level": 7
|
||||
},
|
||||
|
||||
"db": {
|
||||
"db_conns": {
|
||||
"*default": {
|
||||
"db_type": "redis",
|
||||
"db_host": "127.0.0.1",
|
||||
"db_port": 6379,
|
||||
"db_name": "10",
|
||||
"db_user": "cgrates"
|
||||
},
|
||||
"StorDB": {
|
||||
"db_type": "postgres",
|
||||
"db_host": "127.0.0.1",
|
||||
"db_port": 5432,
|
||||
"db_name": "cgrates",
|
||||
"db_user": "cgrates",
|
||||
"db_password": "CGRateS.org"
|
||||
}
|
||||
},
|
||||
"items": {
|
||||
"*cdrs": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false, "dbConn": "StorDB"},
|
||||
"*rate_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false, "dbConn": "StorDB"}
|
||||
}
|
||||
},
|
||||
|
||||
"rates": {
|
||||
"enabled": true,
|
||||
"prefix_indexed_fields": ["*req.Destination"],
|
||||
"exists_indexed_fields": ["*req.Destination"],
|
||||
"rate_prefix_indexed_fields": ["*req.Destination"]
|
||||
},
|
||||
|
||||
"admins": {
|
||||
"enabled": true
|
||||
}
|
||||
|
||||
}
|
||||
45
data/conf/samples/rates_redis/cgrates.json
Normal file
45
data/conf/samples/rates_redis/cgrates.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
|
||||
"general": {
|
||||
"node_id": "id",
|
||||
},
|
||||
|
||||
"logger": {
|
||||
"level": 7
|
||||
},
|
||||
|
||||
"db": {
|
||||
"db_conns": {
|
||||
"*default": {
|
||||
"db_type": "redis",
|
||||
"db_host": "127.0.0.1",
|
||||
"db_port": 6379,
|
||||
"db_name": "10",
|
||||
"db_user": "cgrates"
|
||||
},
|
||||
"StorDB": {
|
||||
"db_type": "mysql",
|
||||
"db_host": "127.0.0.1",
|
||||
"db_port": 3306,
|
||||
"db_name": "cgrates",
|
||||
"db_user": "cgrates",
|
||||
"db_password": "CGRateS.org"
|
||||
}
|
||||
},
|
||||
"items": {
|
||||
"*cdrs": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false, "dbConn": "StorDB"}
|
||||
}
|
||||
},
|
||||
|
||||
"rates": {
|
||||
"enabled": true,
|
||||
"prefix_indexed_fields": ["*req.Destination"],
|
||||
"exists_indexed_fields": ["*req.Destination"],
|
||||
"rate_prefix_indexed_fields": ["*req.Destination"]
|
||||
},
|
||||
|
||||
"admins": {
|
||||
"enabled": true
|
||||
}
|
||||
|
||||
}
|
||||
@@ -154,4 +154,26 @@ CREATE TABLE route_profiles (
|
||||
PRIMARY KEY (`pk`),
|
||||
UNIQUE KEY unique_tenant_id (`tenant`, `id`)
|
||||
);
|
||||
CREATE UNIQUE INDEX route_profiles_idx ON route_profiles (`id`);
|
||||
CREATE UNIQUE INDEX route_profiles_idx ON route_profiles (`id`);
|
||||
|
||||
DROP TABLE IF EXISTS rates;
|
||||
DROP TABLE IF EXISTS rate_profiles;
|
||||
CREATE TABLE rate_profiles (
|
||||
`pk` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`tenant` VARCHAR(40) NOT NULL,
|
||||
`id` VARCHAR(64) NOT NULL,
|
||||
`rate_profile` JSON NOT NULL,
|
||||
PRIMARY KEY (`pk`),
|
||||
UNIQUE KEY unique_tenant_id (`tenant`, `id`)
|
||||
);
|
||||
CREATE UNIQUE INDEX rate_profiles_idx ON rate_profiles (`id`);
|
||||
CREATE TABLE rates (
|
||||
`pk` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`tenant` VARCHAR(40) NOT NULL,
|
||||
`id` VARCHAR(64) NOT NULL,
|
||||
`rate` JSON NOT NULL,
|
||||
`rate_profile_id` VARCHAR(64) NOT NULL,
|
||||
PRIMARY KEY (`pk`),
|
||||
UNIQUE KEY unique_tenant_id_rate_profile_id (`tenant`, `id`, `rate_profile_id`),
|
||||
FOREIGN KEY (rate_profile_id) REFERENCES rate_profiles (id)
|
||||
);
|
||||
@@ -152,3 +152,24 @@ CREATE TABLE route_profiles (
|
||||
UNIQUE (tenant, id)
|
||||
);
|
||||
CREATE UNIQUE INDEX route_profiles_idx ON route_profiles ("id");
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS rates;
|
||||
DROP TABLE IF EXISTS rate_profiles;
|
||||
CREATE TABLE rate_profiles (
|
||||
pk SERIAL PRIMARY KEY,
|
||||
tenant VARCHAR(40) NOT NULL,
|
||||
id VARCHAR(64) NOT NULL,
|
||||
rate_profile JSONB NOT NULL,
|
||||
UNIQUE (tenant, id)
|
||||
);
|
||||
CREATE UNIQUE INDEX rate_profiles_idx ON rate_profiles ("id");
|
||||
CREATE TABLE rates (
|
||||
pk SERIAL PRIMARY KEY,
|
||||
tenant VARCHAR(40) NOT NULL,
|
||||
id VARCHAR(64) NOT NULL,
|
||||
rate JSONB NOT NULL,
|
||||
rate_profile_id VARCHAR(64) NOT NULL,
|
||||
UNIQUE (tenant, id, rate_profile_id),
|
||||
FOREIGN KEY (rate_profile_id) REFERENCES rate_profiles (id)
|
||||
);
|
||||
@@ -536,3 +536,27 @@ type RouteProfileMdl struct {
|
||||
func (RouteProfileMdl) TableName() string {
|
||||
return utils.TBLRouteProfiles
|
||||
}
|
||||
|
||||
// Doesnt include Rates in RateProfile json, Rates taken from Rate using foreign keys
|
||||
type RateProfileJSONMdl struct {
|
||||
PK uint `gorm:"primary_key"`
|
||||
Tenant string `index:"0" re:".*"`
|
||||
ID string `index:"1" re:".*"`
|
||||
RateProfile utils.JSONB `gorm:"type:jsonb" index:"2" re:".*"`
|
||||
}
|
||||
|
||||
func (RateProfileJSONMdl) TableName() string {
|
||||
return utils.TBLRateProfiles
|
||||
}
|
||||
|
||||
type RateMdl struct {
|
||||
PK uint `gorm:"primary_key"`
|
||||
Tenant string `index:"0" re:".*"`
|
||||
ID string `index:"1" re:".*"`
|
||||
Rate utils.JSONB `gorm:"type:jsonb" index:"2" re:".*"`
|
||||
RateProfileID string `gorm:"foreign_key" index:"3" re:".*"`
|
||||
}
|
||||
|
||||
func (RateMdl) TableName() string {
|
||||
return utils.TBLRates
|
||||
}
|
||||
|
||||
@@ -127,6 +127,8 @@ func (sqls *SQLStorage) GetKeysForPrefix(ctx *context.Context, prefix string) (k
|
||||
keys, err = sqls.getAllKeysMatchingTenantID(ctx, utils.TBLFilters, tntID)
|
||||
case utils.RouteProfilePrefix:
|
||||
keys, err = sqls.getAllKeysMatchingTenantID(ctx, utils.TBLRouteProfiles, tntID)
|
||||
case utils.RateProfilePrefix:
|
||||
keys, err = sqls.getAllKeysMatchingTenantID(ctx, utils.TBLRateProfiles, tntID)
|
||||
default:
|
||||
err = fmt.Errorf("unsupported prefix in GetKeysForPrefix: %q", prefix)
|
||||
}
|
||||
@@ -1040,6 +1042,172 @@ func (sqls *SQLStorage) RemoveRouteProfileDrv(ctx *context.Context, tenant, id s
|
||||
return
|
||||
}
|
||||
|
||||
func (sqls *SQLStorage) SetRateProfileDrv(ctx *context.Context, rpp *utils.RateProfile, optOverwrite bool) (err error) {
|
||||
tx := sqls.db.Begin()
|
||||
rpMdl := &RateProfileJSONMdl{
|
||||
Tenant: rpp.Tenant,
|
||||
ID: rpp.ID,
|
||||
RateProfile: rpp.AsMapStringInterface(),
|
||||
}
|
||||
if optOverwrite {
|
||||
if err = tx.Model(&RateMdl{}).Where(&RateMdl{Tenant: rpMdl.Tenant, RateProfileID: rpMdl.ID}).
|
||||
Delete(&RateMdl{}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
if err = tx.Model(&RateProfileJSONMdl{}).Where(
|
||||
RateProfileJSONMdl{Tenant: rpMdl.Tenant, ID: rpMdl.ID}).Delete(
|
||||
RateProfileJSONMdl{}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
}
|
||||
var existingRP RateProfileJSONMdl
|
||||
result := tx.Where(RateProfileJSONMdl{Tenant: rpMdl.Tenant, ID: rpMdl.ID}).First(&existingRP)
|
||||
switch result.Error {
|
||||
case nil: // Record exists, update it
|
||||
rpMdl.PK = existingRP.PK
|
||||
if err = tx.Save(rpMdl).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
case gorm.ErrRecordNotFound: // Record doesn't exist, create it
|
||||
if err = tx.Create(rpMdl).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
default:
|
||||
tx.Rollback()
|
||||
return result.Error
|
||||
}
|
||||
for rID, rate := range rpp.Rates {
|
||||
rMdl := &RateMdl{
|
||||
Tenant: rpp.Tenant,
|
||||
ID: rID,
|
||||
Rate: rate.AsMapStringInterface(),
|
||||
RateProfileID: rpp.ID,
|
||||
}
|
||||
if optOverwrite {
|
||||
if err = tx.Model(&RateMdl{}).Where(
|
||||
RateMdl{Tenant: rMdl.Tenant, ID: rMdl.ID}).Delete(
|
||||
RateMdl{}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
}
|
||||
var existingRT RateMdl
|
||||
result := tx.Where(RateMdl{Tenant: rMdl.Tenant, ID: rMdl.ID, RateProfileID: rpMdl.ID}).First(&existingRT)
|
||||
switch result.Error {
|
||||
case nil: // Record exists, update it
|
||||
rMdl.PK = existingRT.PK
|
||||
if err = tx.Save(rMdl).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
case gorm.ErrRecordNotFound: // Record doesn't exist, create it
|
||||
if err = tx.Create(rMdl).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
default:
|
||||
tx.Rollback()
|
||||
return result.Error
|
||||
}
|
||||
}
|
||||
tx.Commit()
|
||||
return
|
||||
}
|
||||
|
||||
func (sqls *SQLStorage) GetRateProfileDrv(ctx *context.Context, tenant, id string) (rpp *utils.RateProfile, err error) {
|
||||
var rpResult []*RateProfileJSONMdl
|
||||
if err = sqls.db.Model(&RateProfileJSONMdl{}).Where(&RateProfileJSONMdl{Tenant: tenant,
|
||||
ID: id}).Find(&rpResult).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(rpResult) == 0 {
|
||||
return nil, utils.ErrNotFound
|
||||
}
|
||||
|
||||
if rpp, err = utils.MapStringInterfaceToRateProfile(rpResult[0].RateProfile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var rtResult []*RateMdl
|
||||
if err = sqls.db.Model(&RateMdl{}).Where(&RateMdl{Tenant: tenant,
|
||||
RateProfileID: id}).Find(&rtResult).Error; err != nil { // find all rates for that rating profile
|
||||
return nil, err
|
||||
}
|
||||
if len(rtResult) == 0 {
|
||||
return nil, utils.ErrNotFound
|
||||
}
|
||||
for _, rateMdl := range rtResult {
|
||||
if rt, err := utils.MapStringInterfaceToRate(rateMdl.Rate); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
rpp.Rates[rt.ID] = rt
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetRateProfileRatesDrv will return back all the RateIDs and Rates from a RateProfile
|
||||
func (sqls *SQLStorage) GetRateProfileRatesDrv(ctx *context.Context, tnt, profileID, rtPrfx string, needIDs bool) (rateIDs []string, rates []*utils.Rate, err error) {
|
||||
tx := sqls.db.Model(&RateMdl{}).Where(&RateMdl{RateProfileID: profileID})
|
||||
if rtPrfx != utils.EmptyString {
|
||||
tx = tx.Where("id LIKE ?", rtPrfx+"%")
|
||||
}
|
||||
var rtResult []*RateMdl
|
||||
if err = tx.Find(&rtResult).Error; err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if len(rtResult) == 0 {
|
||||
return nil, nil, utils.ErrNotFound
|
||||
}
|
||||
for _, ratesMdl := range rtResult {
|
||||
rateIDs = append(rateIDs, ratesMdl.ID)
|
||||
}
|
||||
if needIDs {
|
||||
// Only return IDs
|
||||
return rateIDs, nil, nil
|
||||
}
|
||||
for _, rateMdl := range rtResult {
|
||||
if rt, err := utils.MapStringInterfaceToRate(rateMdl.Rate); err != nil {
|
||||
return nil, nil, err
|
||||
} else {
|
||||
rates = append(rates, rt)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (sqls *SQLStorage) RemoveRateProfileDrv(ctx *context.Context, tenant, id string, rateIDs *[]string) (err error) {
|
||||
tx := sqls.db.Begin()
|
||||
if rateIDs != nil {
|
||||
for _, rateID := range *rateIDs {
|
||||
if err = tx.Model(&RateMdl{}).Where(&RateMdl{Tenant: tenant, ID: rateID, RateProfileID: id}).
|
||||
Delete(&RateMdl{}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
tx.Commit()
|
||||
return
|
||||
}
|
||||
|
||||
if err = tx.Model(&RateMdl{}).Where(&RateMdl{Tenant: tenant, RateProfileID: id}).
|
||||
Delete(&RateMdl{}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
if err = tx.Model(&RateProfileJSONMdl{}).Where(&RateProfileJSONMdl{Tenant: tenant, ID: id}).
|
||||
Delete(&RateProfileJSONMdl{}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
tx.Commit()
|
||||
return
|
||||
}
|
||||
|
||||
// Used to check if specific subject is stored using prefix key attached to entity
|
||||
func (sqls *SQLStorage) HasDataDrv(ctx *context.Context, category, subject, tenant string) (has bool, err error) {
|
||||
var categoryModelMap = map[string]any{
|
||||
@@ -1057,9 +1225,9 @@ func (sqls *SQLStorage) HasDataDrv(ctx *context.Context, category, subject, tena
|
||||
utils.RouteProfilePrefix: &RouteProfileMdl{},
|
||||
utils.AttributeProfilePrefix: &AttributeProfileMdl{},
|
||||
utils.ChargerProfilePrefix: &ChargerProfileMdl{},
|
||||
utils.RateProfilePrefix: &RateProfileJSONMdl{},
|
||||
// utils.TrendPrefix: &TrendJSONMdl{},
|
||||
// utils.TrendProfilePrefix: &TrendProfileMdl{},
|
||||
// utils.RateProfilePrefix: &RateProfileJSONMdl{},
|
||||
}
|
||||
model, ok := categoryModelMap[category]
|
||||
if !ok {
|
||||
@@ -1189,26 +1357,6 @@ func (sqls *SQLStorage) RemoveLoadIDsDrv() (err error) {
|
||||
return utils.ErrNotImplemented
|
||||
}
|
||||
|
||||
// DataDB method not implemented yet
|
||||
func (sqls *SQLStorage) SetRateProfileDrv(ctx *context.Context, rpp *utils.RateProfile, optOverwrite bool) (err error) {
|
||||
return utils.ErrNotImplemented
|
||||
}
|
||||
|
||||
// DataDB method not implemented yet
|
||||
func (sqls *SQLStorage) GetRateProfileDrv(ctx *context.Context, tenant, id string) (rpp *utils.RateProfile, err error) {
|
||||
return nil, utils.ErrNotImplemented
|
||||
}
|
||||
|
||||
// GetRateProfileRateIDsDrv DataDB method not implemented yet
|
||||
func (sqls *SQLStorage) GetRateProfileRatesDrv(ctx *context.Context, tnt, profileID, rtPrfx string, needIDs bool) (rateIDs []string, rates []*utils.Rate, err error) {
|
||||
return nil, nil, utils.ErrNotImplemented
|
||||
}
|
||||
|
||||
// DataDB method not implemented yet
|
||||
func (sqls *SQLStorage) RemoveRateProfileDrv(ctx *context.Context, tenant, id string, rateIDs *[]string) (err error) {
|
||||
return utils.ErrNotImplemented
|
||||
}
|
||||
|
||||
// GetIndexesDrv DataDB method not implemented yet
|
||||
func (sqls *SQLStorage) GetIndexesDrv(ctx *context.Context, idxItmType, tntCtx, idxKey, transactionID string) (indexes map[string]utils.StringSet, err error) {
|
||||
return nil, utils.ErrNotImplemented
|
||||
|
||||
@@ -1926,6 +1926,8 @@ const (
|
||||
TBLThresholds = "thresholds"
|
||||
TBLFilters = "filters"
|
||||
TBLRouteProfiles = "route_profiles"
|
||||
TBLRateProfiles = "rate_profiles"
|
||||
TBLRates = "rates"
|
||||
OldSMCosts = "sm_costs"
|
||||
TBLTPDispatchers = "tp_dispatcher_profiles"
|
||||
TBLTPDispatcherHosts = "tp_dispatcher_hosts"
|
||||
|
||||
@@ -1026,6 +1026,42 @@ func (rt *Rate) FieldAsInterface(fldPath []string) (_ any, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// AsMapStringInterface converts Rate struct to map[string]any
|
||||
func (rt *Rate) AsMapStringInterface() map[string]any {
|
||||
if rt == nil {
|
||||
return nil
|
||||
}
|
||||
return map[string]any{
|
||||
ID: rt.ID,
|
||||
FilterIDs: rt.FilterIDs,
|
||||
ActivationTimes: rt.ActivationTimes,
|
||||
Weights: rt.Weights,
|
||||
Blocker: rt.Blocker,
|
||||
IntervalRates: rt.IntervalRates,
|
||||
}
|
||||
}
|
||||
|
||||
// MapStringInterfaceToRate converts map[string]any to Rate struct
|
||||
func MapStringInterfaceToRate(m map[string]any) (*Rate, error) {
|
||||
rt := &Rate{}
|
||||
if v, ok := m[ID].(string); ok {
|
||||
rt.ID = v
|
||||
}
|
||||
rt.FilterIDs = InterfaceToStringSlice(m[FilterIDs])
|
||||
if v, ok := m[ActivationTimes].(string); ok {
|
||||
rt.ActivationTimes = v
|
||||
}
|
||||
rt.Weights = InterfaceToDynamicWeights(m[Weights])
|
||||
if v, ok := m[Blocker].(bool); ok {
|
||||
rt.Blocker = v
|
||||
}
|
||||
var err error
|
||||
if rt.IntervalRates, err = InterfaceToIntervalRates(m[IntervalRates]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rt, nil
|
||||
}
|
||||
|
||||
func (iR *IntervalRate) String() string { return ToJSON(iR) }
|
||||
func (iR *IntervalRate) FieldAsString(fldPath []string) (_ string, err error) {
|
||||
var val any
|
||||
@@ -1054,6 +1090,42 @@ func (iR *IntervalRate) FieldAsInterface(fldPath []string) (_ any, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// InterfaceToIntervalRates converts any to []*IntervalRate
|
||||
func InterfaceToIntervalRates(v any) (intervalRates []*IntervalRate, err error) {
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
switch val := v.(type) {
|
||||
case []*IntervalRate:
|
||||
return val, nil
|
||||
case []any:
|
||||
result := make([]*IntervalRate, 0, len(val))
|
||||
for _, item := range val {
|
||||
if irMap, ok := item.(map[string]any); ok {
|
||||
ir := new(IntervalRate)
|
||||
if ir.IntervalStart, err = NewDecimalFromInterface(irMap[IntervalStart]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ir.FixedFee, err = NewDecimalFromInterface(irMap[FixedFee]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ir.RecurrentFee, err = NewDecimalFromInterface(irMap[RecurrentFee]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ir.Unit, err = NewDecimalFromInterface(irMap[Unit]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ir.Increment, err = NewDecimalFromInterface(irMap[Increment]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, ir)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// AsDataDBMap is used to is a convert method in order to properly set trough a hasmap in redis server our rate profile
|
||||
func (rp *RateProfile) AsDataDBMap(ms Marshaler) (mp map[string]any, err error) {
|
||||
mp = map[string]any{
|
||||
@@ -1089,6 +1161,51 @@ func (rp *RateProfile) AsDataDBMap(ms Marshaler) (mp map[string]any, err error)
|
||||
return mp, nil
|
||||
}
|
||||
|
||||
// AsMapStringInterface converts RateProfile struct to map[string]any
|
||||
//
|
||||
// ! Rates not included !
|
||||
func (rp *RateProfile) AsMapStringInterface() map[string]any {
|
||||
if rp == nil {
|
||||
return nil
|
||||
}
|
||||
return map[string]any{
|
||||
Tenant: rp.Tenant,
|
||||
ID: rp.ID,
|
||||
FilterIDs: rp.FilterIDs,
|
||||
Weights: rp.Weights,
|
||||
MinCost: rp.MinCost,
|
||||
MaxCost: rp.MaxCost,
|
||||
MaxCostStrategy: rp.MaxCostStrategy,
|
||||
}
|
||||
}
|
||||
|
||||
// MapStringInterfaceToRateProfile converts map[string]any to RateProfile struct
|
||||
//
|
||||
// ! Rates not included !
|
||||
func MapStringInterfaceToRateProfile(m map[string]any) (*RateProfile, error) {
|
||||
rp := &RateProfile{}
|
||||
if v, ok := m[Tenant].(string); ok {
|
||||
rp.Tenant = v
|
||||
}
|
||||
if v, ok := m[ID].(string); ok {
|
||||
rp.ID = v
|
||||
}
|
||||
rp.FilterIDs = InterfaceToStringSlice(m[FilterIDs])
|
||||
rp.Weights = InterfaceToDynamicWeights(m[Weights])
|
||||
var err error
|
||||
if rp.MinCost, err = NewDecimalFromInterface(m[MinCost]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if rp.MaxCost, err = NewDecimalFromInterface(m[MaxCost]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if v, ok := m[MaxCostStrategy].(string); ok {
|
||||
rp.MaxCostStrategy = v
|
||||
}
|
||||
rp.Rates = make(map[string]*Rate)
|
||||
return rp, nil
|
||||
}
|
||||
|
||||
// NewRateProfileFromMapDataDBMap will convert a RateProfile map into a RatePRofile struct. This is used when we get the map from redis database
|
||||
func NewRateProfileFromMapDataDBMap(tnt, id string, mapRP map[string]any, ms Marshaler) (rp *RateProfile, err error) {
|
||||
rp = &RateProfile{
|
||||
|
||||
Reference in New Issue
Block a user