diff --git a/apier/v1/actionprofiles.go b/apier/v1/actionprofiles.go new file mode 100644 index 000000000..0bfbf539c --- /dev/null +++ b/apier/v1/actionprofiles.go @@ -0,0 +1,141 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +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 v1 + +import ( + "time" + + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +// GetActionProfile returns an Action Profile +func (apierSv1 *APIerSv1) GetActionProfile(arg *utils.TenantIDWithOpts, reply *engine.ActionProfile) error { + if missing := utils.MissingStructFields(arg, []string{utils.ID}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + tnt := arg.Tenant + if tnt == utils.EmptyString { + tnt = apierSv1.Config.GeneralCfg().DefaultTenant + } + ap, err := apierSv1.DataManager.GetActionProfile(tnt, arg.ID, true, true, utils.NonTransactional) + if err != nil { + if err.Error() != utils.ErrNotFound.Error() { + err = utils.NewErrServerError(err) + } + return err + } + *reply = *ap + return nil +} + +// GetActionProfileIDs returns list of action profile IDs registered for a tenant +func (apierSv1 *APIerSv1) GetActionProfileIDs(args *utils.PaginatorWithTenant, actPrfIDs *[]string) error { + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = apierSv1.Config.GeneralCfg().DefaultTenant + } + prfx := utils.RateProfilePrefix + tnt + utils.CONCATENATED_KEY_SEP + keys, err := apierSv1.DataManager.DataDB().GetKeysForPrefix(prfx) + if err != nil { + return err + } + if len(keys) == 0 { + return utils.ErrNotFound + } + retIDs := make([]string, len(keys)) + for i, key := range keys { + retIDs[i] = key[len(prfx):] + } + *actPrfIDs = args.PaginateStringSlice(retIDs) + return nil +} + +// GetActionProfileIDsCount sets in reply var the total number of ActionProfileIDs registered for a tenant +// returns ErrNotFound in case of 0 ActionProfileIDs +func (apierSv1 *APIerSv1) GetActionProfileIDsCount(args *utils.TenantWithOpts, reply *int) (err error) { + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = apierSv1.Config.GeneralCfg().DefaultTenant + } + var keys []string + prfx := utils.ActionProfilePrefix + tnt + utils.CONCATENATED_KEY_SEP + if keys, err = apierSv1.DataManager.DataDB().GetKeysForPrefix(prfx); err != nil { + return err + } + if len(keys) == 0 { + return utils.ErrNotFound + } + *reply = len(keys) + return +} + +type ActionProfileWithCache struct { + *engine.ActionProfileWithOpts + Cache *string +} + +//SetActionProfile add/update a new Action Profile +func (apierSv1 *APIerSv1) SetActionProfile(ap *ActionProfileWithCache, reply *string) error { + if missing := utils.MissingStructFields(ap.ActionProfile, []string{utils.ID, utils.Actions}); len(missing) != 0 { + return utils.NewErrMandatoryIeMissing(missing...) + } + if ap.Tenant == utils.EmptyString { + ap.Tenant = apierSv1.Config.GeneralCfg().DefaultTenant + } + + if err := apierSv1.DataManager.SetActionProfile(ap.ActionProfile, true); err != nil { + return utils.APIErrorHandler(err) + } + //generate a loadID for CacheActionProfiles and store it in database + if err := apierSv1.DataManager.SetLoadIDs(map[string]int64{utils.CacheActionProfiles: time.Now().UnixNano()}); err != nil { + return utils.APIErrorHandler(err) + } + if err := apierSv1.CallCache(ap.Cache, ap.Tenant, utils.CacheActionProfiles, + ap.TenantID(), &ap.FilterIDs, nil, ap.Opts); err != nil { + return utils.APIErrorHandler(err) + } + *reply = utils.OK + return nil +} + +// RemoveActionProfile remove a specific Action Profile +func (apierSv1 *APIerSv1) RemoveActionProfile(arg *utils.TenantIDWithCache, reply *string) error { + if missing := utils.MissingStructFields(arg, []string{utils.ID}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + tnt := arg.Tenant + if tnt == utils.EmptyString { + tnt = apierSv1.Config.GeneralCfg().DefaultTenant + } + if err := apierSv1.DataManager.RemoveActionProfile(tnt, arg.ID, + utils.NonTransactional, true); err != nil { + return utils.APIErrorHandler(err) + } + //generate a loadID for CacheActionProfiles and store it in database + if err := apierSv1.DataManager.SetLoadIDs(map[string]int64{utils.CacheActionProfiles: time.Now().UnixNano()}); err != nil { + return utils.APIErrorHandler(err) + } + if err := apierSv1.CallCache(arg.Cache, tnt, utils.CacheActionProfiles, + utils.ConcatenatedKey(tnt, arg.ID), nil, nil, arg.Opts); err != nil { + return utils.APIErrorHandler(err) + } + *reply = utils.OK + return nil +} diff --git a/apier/v1/apier.go b/apier/v1/apier.go index 95b304ca0..7c7396c1d 100644 --- a/apier/v1/apier.go +++ b/apier/v1/apier.go @@ -250,14 +250,14 @@ type AttrLoadRatingPlan struct { } // Process dependencies and load a specific rating plan from storDb into dataDb. -func (apiv1 *APIerSv1) LoadRatingPlan(attrs *AttrLoadRatingPlan, reply *string) error { +func (apierSv1 *APIerSv1) LoadRatingPlan(attrs *AttrLoadRatingPlan, reply *string) error { if len(attrs.TPid) == 0 { return utils.NewErrMandatoryIeMissing("TPid") } - dbReader, err := engine.NewTpReader(apiv1.DataManager.DataDB(), apiv1.StorDb, - attrs.TPid, apiv1.Config.GeneralCfg().DefaultTimezone, - apiv1.Config.ApierCfg().CachesConns, apiv1.Config.ApierCfg().SchedulerConns, - apiv1.Config.DataDbCfg().DataDbType == utils.INTERNAL) + dbReader, err := engine.NewTpReader(apierSv1.DataManager.DataDB(), apierSv1.StorDb, + attrs.TPid, apierSv1.Config.GeneralCfg().DefaultTimezone, + apierSv1.Config.ApierCfg().CachesConns, apierSv1.Config.ApierCfg().SchedulerConns, + apierSv1.Config.DataDbCfg().DataDbType == utils.INTERNAL) if err != nil { return utils.NewErrServerError(err) } @@ -271,17 +271,17 @@ func (apiv1 *APIerSv1) LoadRatingPlan(attrs *AttrLoadRatingPlan, reply *string) } // Process dependencies and load a specific rating profile from storDb into dataDb. -func (apiv1 *APIerSv1) LoadRatingProfile(attrs *utils.TPRatingProfile, reply *string) error { +func (apierSv1 *APIerSv1) LoadRatingProfile(attrs *utils.TPRatingProfile, reply *string) error { if len(attrs.TPid) == 0 { return utils.NewErrMandatoryIeMissing("TPid") } if attrs.Tenant == utils.EmptyString { - attrs.Tenant = apiv1.Config.GeneralCfg().DefaultTenant + attrs.Tenant = apierSv1.Config.GeneralCfg().DefaultTenant } - dbReader, err := engine.NewTpReader(apiv1.DataManager.DataDB(), apiv1.StorDb, - attrs.TPid, apiv1.Config.GeneralCfg().DefaultTimezone, - apiv1.Config.ApierCfg().CachesConns, apiv1.Config.ApierCfg().SchedulerConns, - apiv1.Config.DataDbCfg().DataDbType == utils.INTERNAL) + dbReader, err := engine.NewTpReader(apierSv1.DataManager.DataDB(), apierSv1.StorDb, + attrs.TPid, apierSv1.Config.GeneralCfg().DefaultTimezone, + apierSv1.Config.ApierCfg().CachesConns, apierSv1.Config.ApierCfg().SchedulerConns, + apierSv1.Config.DataDbCfg().DataDbType == utils.INTERNAL) if err != nil { return utils.NewErrServerError(err) } @@ -298,14 +298,14 @@ type AttrLoadSharedGroup struct { } // Load destinations from storDb into dataDb. -func (apiv1 *APIerSv1) LoadSharedGroup(attrs *AttrLoadSharedGroup, reply *string) error { +func (apierSv1 *APIerSv1) LoadSharedGroup(attrs *AttrLoadSharedGroup, reply *string) error { if len(attrs.TPid) == 0 { return utils.NewErrMandatoryIeMissing("TPid") } - dbReader, err := engine.NewTpReader(apiv1.DataManager.DataDB(), apiv1.StorDb, - attrs.TPid, apiv1.Config.GeneralCfg().DefaultTimezone, - apiv1.Config.ApierCfg().CachesConns, apiv1.Config.ApierCfg().SchedulerConns, - apiv1.Config.DataDbCfg().DataDbType == utils.INTERNAL) + dbReader, err := engine.NewTpReader(apierSv1.DataManager.DataDB(), apierSv1.StorDb, + attrs.TPid, apierSv1.Config.GeneralCfg().DefaultTimezone, + apierSv1.Config.ApierCfg().CachesConns, apierSv1.Config.ApierCfg().SchedulerConns, + apierSv1.Config.DataDbCfg().DataDbType == utils.INTERNAL) if err != nil { return utils.NewErrServerError(err) } @@ -325,14 +325,14 @@ type AttrLoadTpFromStorDb struct { } // Loads complete data in a TP from storDb -func (apiv1 *APIerSv1) LoadTariffPlanFromStorDb(attrs *AttrLoadTpFromStorDb, reply *string) error { +func (apierSv1 *APIerSv1) LoadTariffPlanFromStorDb(attrs *AttrLoadTpFromStorDb, reply *string) error { if len(attrs.TPid) == 0 { return utils.NewErrMandatoryIeMissing("TPid") } - dbReader, err := engine.NewTpReader(apiv1.DataManager.DataDB(), apiv1.StorDb, - attrs.TPid, apiv1.Config.GeneralCfg().DefaultTimezone, - apiv1.Config.ApierCfg().CachesConns, apiv1.Config.ApierCfg().SchedulerConns, - apiv1.Config.DataDbCfg().DataDbType == utils.INTERNAL) + dbReader, err := engine.NewTpReader(apierSv1.DataManager.DataDB(), apierSv1.StorDb, + attrs.TPid, apierSv1.Config.GeneralCfg().DefaultTimezone, + apierSv1.Config.ApierCfg().CachesConns, apierSv1.Config.ApierCfg().SchedulerConns, + apierSv1.Config.DataDbCfg().DataDbType == utils.INTERNAL) if err != nil { return utils.NewErrServerError(err) } @@ -362,7 +362,7 @@ func (apiv1 *APIerSv1) LoadTariffPlanFromStorDb(attrs *AttrLoadTpFromStorDb, rep if err := dbReader.ReloadCache(caching, true, attrs.Opts); err != nil { return utils.NewErrServerError(err) } - if len(apiv1.Config.ApierCfg().SchedulerConns) != 0 { + if len(apierSv1.Config.ApierCfg().SchedulerConns) != 0 { utils.Logger.Info("APIerSv1.LoadTariffPlanFromStorDb, reloading scheduler.") if err := dbReader.ReloadScheduler(true); err != nil { return utils.NewErrServerError(err) @@ -979,7 +979,7 @@ func (apierSv1 *APIerSv1) LoadAccountActions(attrs *utils.TPAccountActions, repl return nil } -func (apiv1 *APIerSv1) LoadTariffPlanFromFolder(attrs *utils.AttrLoadTpFromFolder, reply *string) error { +func (apierSv1 *APIerSv1) LoadTariffPlanFromFolder(attrs *utils.AttrLoadTpFromFolder, reply *string) error { // verify if FolderPath is present if len(attrs.FolderPath) == 0 { return fmt.Errorf("%s:%s", utils.ErrMandatoryIeMissing.Error(), "FolderPath") @@ -995,11 +995,11 @@ func (apiv1 *APIerSv1) LoadTariffPlanFromFolder(attrs *utils.AttrLoadTpFromFolde } // create the TpReader - loader, err := engine.NewTpReader(apiv1.DataManager.DataDB(), + loader, err := engine.NewTpReader(apierSv1.DataManager.DataDB(), engine.NewFileCSVStorage(utils.CSV_SEP, attrs.FolderPath), - "", apiv1.Config.GeneralCfg().DefaultTimezone, - apiv1.Config.ApierCfg().CachesConns, apiv1.Config.ApierCfg().SchedulerConns, - apiv1.Config.DataDbCfg().DataDbType == utils.INTERNAL) + "", apierSv1.Config.GeneralCfg().DefaultTimezone, + apierSv1.Config.ApierCfg().CachesConns, apierSv1.Config.ApierCfg().SchedulerConns, + apierSv1.Config.DataDbCfg().DataDbType == utils.INTERNAL) if err != nil { return utils.NewErrServerError(err) } @@ -1032,7 +1032,7 @@ func (apiv1 *APIerSv1) LoadTariffPlanFromFolder(attrs *utils.AttrLoadTpFromFolde if err := loader.ReloadCache(caching, true, attrs.Opts); err != nil { return utils.NewErrServerError(err) } - if len(apiv1.Config.ApierCfg().SchedulerConns) != 0 { + if len(apierSv1.Config.ApierCfg().SchedulerConns) != 0 { utils.Logger.Info("APIerSv1.LoadTariffPlanFromFolder, reloading scheduler.") if err := loader.ReloadScheduler(true); err != nil { return utils.NewErrServerError(err) @@ -1046,7 +1046,7 @@ func (apiv1 *APIerSv1) LoadTariffPlanFromFolder(attrs *utils.AttrLoadTpFromFolde // RemoveTPFromFolder will load the tarrifplan from folder into TpReader object // and will delete if from database -func (apiv1 *APIerSv1) RemoveTPFromFolder(attrs *utils.AttrLoadTpFromFolder, reply *string) error { +func (apierSv1 *APIerSv1) RemoveTPFromFolder(attrs *utils.AttrLoadTpFromFolder, reply *string) error { // verify if FolderPath is present if len(attrs.FolderPath) == 0 { return fmt.Errorf("%s:%s", utils.ErrMandatoryIeMissing.Error(), "FolderPath") @@ -1062,10 +1062,10 @@ func (apiv1 *APIerSv1) RemoveTPFromFolder(attrs *utils.AttrLoadTpFromFolder, rep } // create the TpReader - loader, err := engine.NewTpReader(apiv1.DataManager.DataDB(), - engine.NewFileCSVStorage(utils.CSV_SEP, attrs.FolderPath), "", apiv1.Config.GeneralCfg().DefaultTimezone, - apiv1.Config.ApierCfg().CachesConns, apiv1.Config.ApierCfg().SchedulerConns, - apiv1.Config.DataDbCfg().DataDbType == utils.INTERNAL) + loader, err := engine.NewTpReader(apierSv1.DataManager.DataDB(), + engine.NewFileCSVStorage(utils.CSV_SEP, attrs.FolderPath), "", apierSv1.Config.GeneralCfg().DefaultTimezone, + apierSv1.Config.ApierCfg().CachesConns, apierSv1.Config.ApierCfg().SchedulerConns, + apierSv1.Config.DataDbCfg().DataDbType == utils.INTERNAL) if err != nil { return utils.NewErrServerError(err) } @@ -1098,7 +1098,7 @@ func (apiv1 *APIerSv1) RemoveTPFromFolder(attrs *utils.AttrLoadTpFromFolder, rep if err := loader.ReloadCache(caching, true, attrs.Opts); err != nil { return utils.NewErrServerError(err) } - if len(apiv1.Config.ApierCfg().SchedulerConns) != 0 { + if len(apierSv1.Config.ApierCfg().SchedulerConns) != 0 { utils.Logger.Info("APIerSv1.RemoveTPFromFolder, reloading scheduler.") if err := loader.ReloadScheduler(true); err != nil { return utils.NewErrServerError(err) @@ -1112,14 +1112,14 @@ func (apiv1 *APIerSv1) RemoveTPFromFolder(attrs *utils.AttrLoadTpFromFolder, rep // RemoveTPFromStorDB will load the tarrifplan from StorDB into TpReader object // and will delete if from database -func (apiv1 *APIerSv1) RemoveTPFromStorDB(attrs *AttrLoadTpFromStorDb, reply *string) error { +func (apierSv1 *APIerSv1) RemoveTPFromStorDB(attrs *AttrLoadTpFromStorDb, reply *string) error { if len(attrs.TPid) == 0 { return utils.NewErrMandatoryIeMissing("TPid") } - dbReader, err := engine.NewTpReader(apiv1.DataManager.DataDB(), apiv1.StorDb, - attrs.TPid, apiv1.Config.GeneralCfg().DefaultTimezone, - apiv1.Config.ApierCfg().CachesConns, apiv1.Config.ApierCfg().SchedulerConns, - apiv1.Config.DataDbCfg().DataDbType == utils.INTERNAL) + dbReader, err := engine.NewTpReader(apierSv1.DataManager.DataDB(), apierSv1.StorDb, + attrs.TPid, apierSv1.Config.GeneralCfg().DefaultTimezone, + apierSv1.Config.ApierCfg().CachesConns, apierSv1.Config.ApierCfg().SchedulerConns, + apierSv1.Config.DataDbCfg().DataDbType == utils.INTERNAL) if err != nil { return utils.NewErrServerError(err) } @@ -1150,7 +1150,7 @@ func (apiv1 *APIerSv1) RemoveTPFromStorDB(attrs *AttrLoadTpFromStorDb, reply *st if err := dbReader.ReloadCache(caching, true, attrs.Opts); err != nil { return utils.NewErrServerError(err) } - if len(apiv1.Config.ApierCfg().SchedulerConns) != 0 { + if len(apierSv1.Config.ApierCfg().SchedulerConns) != 0 { utils.Logger.Info("APIerSv1.RemoveTPFromStorDB, reloading scheduler.") if err := dbReader.ReloadScheduler(true); err != nil { return utils.NewErrServerError(err) diff --git a/data/storage/mysql/create_tariffplan_tables.sql b/data/storage/mysql/create_tariffplan_tables.sql index 2b841f646..1ee2b5321 100644 --- a/data/storage/mysql/create_tariffplan_tables.sql +++ b/data/storage/mysql/create_tariffplan_tables.sql @@ -505,6 +505,37 @@ CREATE TABLE tp_rate_profiles ( `id`,`filter_ids`,`rate_id` ) ); +-- +-- Table structure for table `tp_action_profiles` +-- + + +DROP TABLE IF EXISTS tp_action_profiles; +CREATE TABLE tp_action_profiles ( + `pk` int(11) NOT NULL AUTO_INCREMENT, + `tpid` varchar(64) NOT NULL, + `tenant` varchar(64) NOT NULL, + `id` varchar(64) NOT NULL, + `filter_ids` varchar(64) NOT NULL, + `activation_interval` varchar(64) NOT NULL, + `weight` decimal(8,2) NOT NULL, + `schedule` varchar(64) NOT NULL, + `account_ids` varchar(64) NOT NULL, + `action_id` varchar(64) NOT NULL, + `action_filter_ids` varchar(64) NOT NULL, + `action_blocker` BOOLEAN NOT NULL, + `action_ttl` varchar(64) NOT NULL, + `action_type` varchar(64) NOT NULL, + `action_opts` varchar(64) NOT NULL, + `action_path` varchar(64) NOT NULL, + `action_value` varchar(64) NOT NULL, + `created_at` TIMESTAMP, + PRIMARY KEY (`pk`), + KEY `tpid` (`tpid`), + UNIQUE KEY `unique_tp_action_profiles` (`tpid`,`tenant`, + `id`,`filter_ids`,`action_id` ) +); + -- -- Table structure for table `versions` -- diff --git a/data/storage/postgres/create_tariffplan_tables.sql b/data/storage/postgres/create_tariffplan_tables.sql index dffe7f234..48cfb1bf1 100644 --- a/data/storage/postgres/create_tariffplan_tables.sql +++ b/data/storage/postgres/create_tariffplan_tables.sql @@ -490,6 +490,37 @@ CREATE INDEX tp_routes_unique ON tp_routes ("tpid", "tenant", "id", CREATE INDEX tp_rate_profiles_unique ON tp_rate_profiles ("tpid", "tenant", "id", "filter_ids", "rate_id"); +-- +-- Table structure for table `tp_action_profiles` +-- + + +DROP TABLE IF EXISTS tp_action_profiles; +CREATE TABLE tp_action_profiles ( + "pk" SERIAL PRIMARY KEY, + "tpid" varchar(64) NOT NULL, + "tenant" varchar(64) NOT NULL, + "id" varchar(64) NOT NULL, + "filter_ids" varchar(64) NOT NULL, + "activation_interval" varchar(64) NOT NULL, + "weight" decimal(8,2) NOT NULL, + "schedule" varchar(64) NOT NULL, + "account_ids" varchar(64) NOT NULL, + "action_id" varchar(64) NOT NULL, + "action_filter_ids" varchar(64) NOT NULL, + "action_blocker" BOOLEAN NOT NULL, + "action_ttl" varchar(64) NOT NULL, + "action_type" varchar(64) NOT NULL, + "action_opts" varchar(64) NOT NULL, + "action_path" varchar(64) NOT NULL, + "action_value" varchar(64) NOT NULL, + "created_at" TIMESTAMP WITH TIME ZONE + ); + CREATE INDEX tp_action_profiles_ids ON tp_action_profiles (tpid); + CREATE INDEX tp_action_profiles_unique ON tp_action_profiles ("tpid", "tenant", "id", + "filter_ids", "action_id"); + + -- -- Table structure for table `versions` -- diff --git a/engine/actionprofile.go b/engine/actionprofile.go index d56eb6511..aa3c65c8b 100644 --- a/engine/actionprofile.go +++ b/engine/actionprofile.go @@ -53,3 +53,8 @@ type APAction struct { Path string // Path to execute Value config.RSRParsers // Value to execute on path } + +type ActionProfileWithOpts struct { + *ActionProfile + Opts map[string]interface{} +} diff --git a/engine/caches.go b/engine/caches.go index 5c6255d9b..9f4f3f3ba 100644 --- a/engine/caches.go +++ b/engine/caches.go @@ -65,6 +65,9 @@ func init() { // RateProfiles gob.Register(new(RateProfile)) gob.Register(new(RateProfileWithOpts)) + // ActionProfiles + gob.Register(new(ActionProfile)) + gob.Register(new(ActionProfileWithOpts)) // CDRs gob.Register(new(EventCost)) diff --git a/engine/datamanager.go b/engine/datamanager.go index bfb4b8a54..857e7e9ef 100644 --- a/engine/datamanager.go +++ b/engine/datamanager.go @@ -3329,6 +3329,138 @@ func (dm *DataManager) SetRateProfileRates(rpp *RateProfile, withIndex bool) (er return } +func (dm *DataManager) GetActionProfile(tenant, id string, cacheRead, cacheWrite bool, + transactionID string) (ap *ActionProfile, err error) { + tntID := utils.ConcatenatedKey(tenant, id) + if cacheRead { + if x, ok := Cache.Get(utils.CacheActionProfiles, tntID); ok { + if x == nil { + return nil, utils.ErrNotFound + } + return x.(*ActionProfile), nil + } + } + if dm == nil { + err = utils.ErrNoDatabaseConn + return + } + ap, err = dm.dataDB.GetActionProfileDrv(tenant, id) + if err != nil { + if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaActionProfiles]; err == utils.ErrNotFound && itm.Remote { + if err = dm.connMgr.Call(config.CgrConfig().DataDbCfg().RmtConns, nil, + utils.ReplicatorSv1GetActionProfile, + &utils.TenantIDWithOpts{ + TenantID: &utils.TenantID{Tenant: tenant, ID: id}, + Opts: map[string]interface{}{ + utils.OptsAPIKey: itm.APIKey, + utils.OptsRouteID: itm.RouteID, + }}, &ap); err == nil { + err = dm.dataDB.SetActionProfileDrv(ap) + } + } + if err != nil { + err = utils.CastRPCErr(err) + if err == utils.ErrNotFound && cacheWrite { + if errCh := Cache.Set(utils.CacheActionProfiles, tntID, nil, nil, + cacheCommit(transactionID), transactionID); errCh != nil { + return nil, errCh + } + + } + return nil, err + } + } + if cacheWrite { + if errCh := Cache.Set(utils.CacheActionProfiles, tntID, ap, nil, + cacheCommit(transactionID), transactionID); errCh != nil { + return nil, errCh + } + } + return +} + +func (dm *DataManager) SetActionProfile(ap *ActionProfile, withIndex bool) (err error) { + if dm == nil { + err = utils.ErrNoDatabaseConn + return + } + if withIndex { + if brokenReference := dm.checkFilters(ap.Tenant, ap.FilterIDs); len(brokenReference) != 0 { + // if we get a broken filter do not set the profile + return fmt.Errorf("broken reference to filter: %+v for item with ID: %+v", + brokenReference, ap.TenantID()) + } + } + oldRpp, err := dm.GetActionProfile(ap.Tenant, ap.ID, true, false, utils.NonTransactional) + if err != nil && err != utils.ErrNotFound { + return err + } + if err = dm.DataDB().SetActionProfileDrv(ap); err != nil { + return err + } + if withIndex { + var oldFiltersIDs *[]string + if oldRpp != nil { + oldFiltersIDs = &oldRpp.FilterIDs + } + if err := updatedIndexes(dm, utils.CacheActionProfilesFilterIndexes, ap.Tenant, + utils.EmptyString, ap.ID, oldFiltersIDs, ap.FilterIDs); err != nil { + return err + } + } + if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaActionProfiles]; itm.Replicate { + var reply string + if err = dm.connMgr.Call(config.CgrConfig().DataDbCfg().RplConns, nil, + utils.ReplicatorSv1SetActionProfile, + &ActionProfileWithOpts{ + ActionProfile: ap, + Opts: map[string]interface{}{ + utils.OptsAPIKey: itm.APIKey, + utils.OptsRouteID: itm.RouteID, + }}, &reply); err != nil { + err = utils.CastRPCErr(err) + return + } + } + return +} + +func (dm *DataManager) RemoveActionProfile(tenant, id string, + transactionID string, withIndex bool) (err error) { + if dm == nil { + err = utils.ErrNoDatabaseConn + return + } + oldRpp, err := dm.GetActionProfile(tenant, id, true, false, utils.NonTransactional) + if err != nil && err != utils.ErrNotFound { + return err + } + if err = dm.DataDB().RemoveActionProfileDrv(tenant, id); err != nil { + return + } + if oldRpp == nil { + return utils.ErrNotFound + } + if withIndex { + if err = removeItemFromFilterIndex(dm, utils.CacheActionProfilesFilterIndexes, + tenant, utils.EmptyString, id, oldRpp.FilterIDs); err != nil { + return + } + } + if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaActionProfiles]; itm.Replicate { + var reply string + dm.connMgr.Call(config.CgrConfig().DataDbCfg().RplConns, nil, + utils.ReplicatorSv1RemoveActionProfile, + &utils.TenantIDWithOpts{ + TenantID: &utils.TenantID{Tenant: tenant, ID: id}, + Opts: map[string]interface{}{ + utils.OptsAPIKey: itm.APIKey, + utils.OptsRouteID: itm.RouteID, + }}, &reply) + } + return +} + // Reconnect reconnects to the DB when the config was changed func (dm *DataManager) Reconnect(marshaller string, newcfg *config.DataDbCfg) (err error) { d, err := NewDataDBConn(newcfg.DataDbType, newcfg.DataDbHost, newcfg.DataDbPort, newcfg.DataDbName, diff --git a/engine/libtest.go b/engine/libtest.go index ab3ef167c..11de6e617 100644 --- a/engine/libtest.go +++ b/engine/libtest.go @@ -286,6 +286,9 @@ cgrates.org,RP1,*string:~*req.Subject:1001,,0,*up,4,0.1,0.6,*free,RT_WEEK,,"* * cgrates.org,RP1,,,,,,,,,RT_WEEK,,,,,1m,1.234,0.06,1m,1s cgrates.org,RP1,,,,,,,,,RT_WEEKEND,,"* * * * 0,6",10,false,0s,0.089,0.06,1m,1s cgrates.org,RP1,,,,,,,,,RT_CHRISTMAS,,* * 24 12 *,30,false,0s,0.0564,0.06,1m,1s +` + ActionProfileCSVContent = ` +#Tenant,ID,FilterIDs,ActivationInterval,Weight,Schedule,AccountIDs,ActionID,ActionFilterIDs,ActionBlocker,ActionTTL,ActionType,ActionOpts,ActionPath,ActionValue ` ) diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 99ce200ad..806e52629 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -42,7 +42,7 @@ func init() { ActionsCSVContent, ActionPlansCSVContent, ActionTriggersCSVContent, AccountActionsCSVContent, ResourcesCSVContent, StatsCSVContent, ThresholdsCSVContent, FiltersCSVContent, RoutesCSVContent, AttributesCSVContent, ChargersCSVContent, DispatcherCSVContent, - DispatcherHostCSVContent, RateProfileCSVContent), testTPID, "", nil, nil, false) + DispatcherHostCSVContent, RateProfileCSVContent, ActionProfileCSVContent), testTPID, "", nil, nil, false) if err != nil { log.Print("error when creating TpReader:", err) } diff --git a/engine/model_helpers.go b/engine/model_helpers.go index 49ff88743..69879f657 100644 --- a/engine/model_helpers.go +++ b/engine/model_helpers.go @@ -3430,7 +3430,7 @@ func APItoModelTPActionProfile(tPrf *utils.TPActionProfile) (mdls ActionProfileM mdl.ActionBlocker = action.Blocker mdl.ActionTTL = action.TTL mdl.ActionType = action.Type - // convert opts + mdl.ActionOpts = action.Opts mdl.ActionPath = action.Path mdl.ActionValue = action.Value mdls = append(mdls, mdl) @@ -3439,12 +3439,92 @@ func APItoModelTPActionProfile(tPrf *utils.TPActionProfile) (mdls ActionProfileM return } -func APItoActionProfile(tpRp *utils.TPActionProfile, timezone string) (rp *ActionProfile, err error) { - - return rp, nil -} - -func ActionProfileToAPI(rp *ActionProfile) (tpRp *utils.TPActionProfile) { - +func APItoActionProfile(tpAp *utils.TPActionProfile, timezone string) (ap *ActionProfile, err error) { + ap = &ActionProfile{ + Tenant: tpAp.Tenant, + ID: tpAp.ID, + FilterIDs: make([]string, len(tpAp.FilterIDs)), + Weight: tpAp.Weight, + Schedule: tpAp.Schedule, + AccountIDs: utils.NewStringSet(tpAp.AccountIDs), + Actions: make([]*APAction, len(tpAp.Actions)), + } + for i, stp := range tpAp.FilterIDs { + ap.FilterIDs[i] = stp + } + if tpAp.ActivationInterval != nil { + if ap.ActivationInterval, err = tpAp.ActivationInterval.AsActivationInterval(timezone); err != nil { + return + } + } + for i, act := range tpAp.Actions { + if act.Path == utils.EmptyString { + err = fmt.Errorf("empty path in ActionProfile <%s> for action <%s>", ap.TenantID(), act.ID) + return + } + ap.Actions[i] = &APAction{ + ID: act.ID, + FilterIDs: act.FilterIDs, + Blocker: act.Blocker, + Type: act.Type, + Path: act.Path, + } + if ap.Actions[i].TTL, err = utils.ParseDurationWithNanosecs(act.TTL); err != nil { + return + } + if act.Opts != utils.EmptyString { + ap.Actions[i].Opts = make(map[string]interface{}) + for _, opt := range strings.Split(act.Opts, utils.INFIELD_SEP) { // example of opts: key1:val1;key2:val2;key3:val3 + keyValSls := utils.SplitConcatenatedKey(opt) + if len(keyValSls) != 2 { + err = fmt.Errorf("malformed option for ActionProfile <%s> for action <%s>", ap.TenantID(), act.ID) + return + } + ap.Actions[i].Opts[keyValSls[0]] = keyValSls[1] + } + } + if ap.Actions[i].Value, err = config.NewRSRParsers(act.Value, config.CgrConfig().GeneralCfg().RSRSep); err != nil { + return + } + } + return +} + +func ActionProfileToAPI(ap *ActionProfile) (tpAp *utils.TPActionProfile) { + tpAp = &utils.TPActionProfile{ + Tenant: ap.Tenant, + ID: ap.ID, + Weight: ap.Weight, + Schedule: ap.Schedule, + AccountIDs: ap.AccountIDs.AsSlice(), + Actions: make([]*utils.TPAPAction, len(ap.Actions)), + } + for i, fli := range ap.FilterIDs { + tpAp.FilterIDs[i] = fli + } + if ap.ActivationInterval != nil { + if !ap.ActivationInterval.ActivationTime.IsZero() { + tpAp.ActivationInterval.ActivationTime = ap.ActivationInterval.ActivationTime.Format(time.RFC3339) + } + if !ap.ActivationInterval.ExpiryTime.IsZero() { + tpAp.ActivationInterval.ExpiryTime = ap.ActivationInterval.ExpiryTime.Format(time.RFC3339) + } + } + for i, act := range ap.Actions { + tpAp.Actions[i] = &utils.TPAPAction{ + ID: act.ID, + FilterIDs: act.FilterIDs, + Blocker: act.Blocker, + TTL: act.TTL.String(), + Type: act.Type, + Path: act.Path, + Value: act.Value.GetRule(config.CgrConfig().GeneralCfg().RSRSep), + } + elems := make([]string, len(act.Opts)) + for k, v := range act.Opts { + elems = append(elems, utils.ConcatenatedKey(k, utils.IfaceAsString(v))) + } + tpAp.Actions[i].Opts = strings.Join(elems, utils.INFIELD_SEP) + } return } diff --git a/engine/storage_csv.go b/engine/storage_csv.go index 9ee5427ac..e80565ed3 100644 --- a/engine/storage_csv.go +++ b/engine/storage_csv.go @@ -66,6 +66,7 @@ type CSVStorage struct { dispatcherProfilesFn []string dispatcherHostsFn []string rateProfilesFn []string + actionProfilesFn []string } // NewCSVStorage creates a CSV storege that takes the data from the paths specified @@ -73,9 +74,9 @@ func NewCSVStorage(sep rune, destinationsFn, timingsFn, ratesFn, destinationratesFn, destinationratetimingsFn, ratingprofilesFn, sharedgroupsFn, actionsFn, actiontimingsFn, actiontriggersFn, accountactionsFn, - resProfilesFn, statsFn, thresholdsFn, - filterFn, routeProfilesFn, attributeProfilesFn, - chargerProfilesFn, dispatcherProfilesFn, dispatcherHostsFn, rateProfilesFn []string) *CSVStorage { + resProfilesFn, statsFn, thresholdsFn, filterFn, routeProfilesFn, + attributeProfilesFn, chargerProfilesFn, dispatcherProfilesFn, dispatcherHostsFn, + rateProfilesFn, actionProfilesFn []string) *CSVStorage { return &CSVStorage{ sep: sep, generator: NewCsvFile, @@ -100,6 +101,7 @@ func NewCSVStorage(sep rune, dispatcherProfilesFn: dispatcherProfilesFn, dispatcherHostsFn: dispatcherHostsFn, rateProfilesFn: rateProfilesFn, + actionProfilesFn: actionProfilesFn, } } @@ -130,6 +132,7 @@ func NewFileCSVStorage(sep rune, dataPath string) *CSVStorage { dispatcherprofilesPaths := appendName(allFoldersPath, utils.DispatcherProfilesCsv) dispatcherhostsPaths := appendName(allFoldersPath, utils.DispatcherHostsCsv) rateProfilesFn := appendName(allFoldersPath, utils.RateProfilesCsv) + actionProfilesFn := append(allFoldersPath, utils.ActionProfilesCsv) return NewCSVStorage(sep, destinationsPaths, timingsPaths, @@ -152,6 +155,7 @@ func NewFileCSVStorage(sep rune, dataPath string) *CSVStorage { dispatcherprofilesPaths, dispatcherhostsPaths, rateProfilesFn, + actionProfilesFn, ) } @@ -159,18 +163,18 @@ func NewFileCSVStorage(sep rune, dataPath string) *CSVStorage { func NewStringCSVStorage(sep rune, destinationsFn, timingsFn, ratesFn, destinationratesFn, destinationratetimingsFn, ratingprofilesFn, sharedgroupsFn, - actionsFn, actiontimingsFn, actiontriggersFn, - accountactionsFn, resProfilesFn, statsFn, - thresholdsFn, filterFn, routeProfilesFn, - attributeProfilesFn, chargerProfilesFn, - dispatcherProfilesFn, dispatcherHostsFn, rateProfilesFn string) *CSVStorage { + actionsFn, actiontimingsFn, actiontriggersFn, accountactionsFn, + resProfilesFn, statsFn, thresholdsFn, filterFn, routeProfilesFn, + attributeProfilesFn, chargerProfilesFn, dispatcherProfilesFn, dispatcherHostsFn, + rateProfilesFn, actionProfilesFn string) *CSVStorage { c := NewCSVStorage(sep, []string{destinationsFn}, []string{timingsFn}, []string{ratesFn}, []string{destinationratesFn}, []string{destinationratetimingsFn}, []string{ratingprofilesFn}, []string{sharedgroupsFn}, []string{actionsFn}, []string{actiontimingsFn}, []string{actiontriggersFn}, []string{accountactionsFn}, []string{resProfilesFn}, []string{statsFn}, []string{thresholdsFn}, []string{filterFn}, []string{routeProfilesFn}, []string{attributeProfilesFn}, []string{chargerProfilesFn}, - []string{dispatcherProfilesFn}, []string{dispatcherHostsFn}, []string{rateProfilesFn}) + []string{dispatcherProfilesFn}, []string{dispatcherHostsFn}, []string{rateProfilesFn}, + []string{actionProfilesFn}) c.generator = NewCsvString return c } @@ -212,7 +216,8 @@ func NewGoogleCSVStorage(sep rune, spreadsheetID string) (*CSVStorage, error) { getIfExist(utils.Chargers), getIfExist(utils.DispatcherProfiles), getIfExist(utils.DispatcherHosts), - getIfExist(utils.RateProfiles)) + getIfExist(utils.RateProfiles), + getIfExist(utils.ActionProfiles)) c.generator = func() csvReaderCloser { return &csvGoogle{ spreadsheetID: spreadsheetID, @@ -245,6 +250,7 @@ func NewURLCSVStorage(sep rune, dataPath string) *CSVStorage { var dispatcherprofilesPaths []string var dispatcherhostsPaths []string var rateProfilesPaths []string + var actionProfilesPaths []string for _, baseURL := range strings.Split(dataPath, utils.INFIELD_SEP) { if !strings.HasSuffix(baseURL, utils.CSVSuffix) { @@ -269,6 +275,7 @@ func NewURLCSVStorage(sep rune, dataPath string) *CSVStorage { dispatcherprofilesPaths = append(dispatcherprofilesPaths, joinURL(baseURL, utils.DispatcherProfilesCsv)) dispatcherhostsPaths = append(dispatcherhostsPaths, joinURL(baseURL, utils.DispatcherHostsCsv)) rateProfilesPaths = append(rateProfilesPaths, joinURL(baseURL, utils.RateProfilesCsv)) + actionProfilesPaths = append(actionProfilesPaths, joinURL(baseURL, utils.ActionProfilesCsv)) continue } switch { @@ -314,6 +321,8 @@ func NewURLCSVStorage(sep rune, dataPath string) *CSVStorage { dispatcherhostsPaths = append(dispatcherhostsPaths, baseURL) case strings.HasSuffix(baseURL, utils.RateProfilesCsv): rateProfilesPaths = append(rateProfilesPaths, baseURL) + case strings.HasSuffix(baseURL, utils.ActionProfilesCsv): + actionProfilesPaths = append(actionProfilesPaths, baseURL) } } @@ -339,6 +348,7 @@ func NewURLCSVStorage(sep rune, dataPath string) *CSVStorage { dispatcherprofilesPaths, dispatcherhostsPaths, rateProfilesPaths, + actionProfilesPaths, ) c.generator = func() csvReaderCloser { return &csvURL{} @@ -665,6 +675,18 @@ func (csvs *CSVStorage) GetTPRateProfiles(tpid, tenant, id string) ([]*utils.TPR return tpDPPs.AsTPRateProfile(), nil } +func (csvs *CSVStorage) GetTPActionProfiles(tpid, tenant, id string) ([]*utils.TPActionProfile, error) { + var tpDPPs ActionProfileMdls + if err := csvs.proccesData(ActionProfileMdl{}, csvs.actionProfilesFn, func(tp interface{}) { + dpp := tp.(ActionProfileMdl) + dpp.Tpid = tpid + tpDPPs = append(tpDPPs, &dpp) + }); err != nil { + return nil, err + } + return tpDPPs.AsTPActionProfile(), nil +} + func (csvs *CSVStorage) GetTpIds(colName string) ([]string, error) { return nil, utils.ErrNotImplemented } diff --git a/engine/storage_interface.go b/engine/storage_interface.go index afe053c77..8159f0455 100644 --- a/engine/storage_interface.go +++ b/engine/storage_interface.go @@ -133,6 +133,9 @@ type DataDB interface { GetRateProfileDrv(string, string) (*RateProfile, error) SetRateProfileDrv(*RateProfile) error RemoveRateProfileDrv(string, string) error + GetActionProfileDrv(string, string) (*ActionProfile, error) + SetActionProfileDrv(*ActionProfile) error + RemoveActionProfileDrv(string, string) error } type StorDB interface { @@ -183,6 +186,7 @@ type LoadReader interface { GetTPDispatcherProfiles(string, string, string) ([]*utils.TPDispatcherProfile, error) GetTPDispatcherHosts(string, string, string) ([]*utils.TPDispatcherHost, error) GetTPRateProfiles(string, string, string) ([]*utils.TPRateProfile, error) + GetTPActionProfiles(string, string, string) ([]*utils.TPActionProfile, error) } type LoadWriter interface { @@ -208,6 +212,7 @@ type LoadWriter interface { SetTPDispatcherProfiles([]*utils.TPDispatcherProfile) error SetTPDispatcherHosts([]*utils.TPDispatcherHost) error SetTPRateProfiles([]*utils.TPRateProfile) error + SetTPActionProfiles([]*utils.TPActionProfile) error } // NewMarshaler returns the marshaler type selected by mrshlerStr diff --git a/engine/storage_internal_datadb.go b/engine/storage_internal_datadb.go index a41cae8e2..23254daee 100644 --- a/engine/storage_internal_datadb.go +++ b/engine/storage_internal_datadb.go @@ -960,6 +960,26 @@ func (iDB *InternalDB) RemoveRateProfileDrv(tenant, id string) (err error) { return } +func (iDB *InternalDB) GetActionProfileDrv(tenant, id string) (ap *ActionProfile, err error) { + x, ok := Cache.Get(utils.CacheActionProfiles, utils.ConcatenatedKey(tenant, id)) + if !ok || x == nil { + return nil, utils.ErrNotFound + } + return x.(*ActionProfile), nil +} + +func (iDB *InternalDB) SetActionProfileDrv(ap *ActionProfile) (err error) { + Cache.SetWithoutReplicate(utils.CacheActionProfiles, ap.TenantID(), ap, nil, + cacheCommit(utils.NonTransactional), utils.NonTransactional) + return +} + +func (iDB *InternalDB) RemoveActionProfileDrv(tenant, id string) (err error) { + Cache.RemoveWithoutReplicate(utils.CacheActionProfiles, utils.ConcatenatedKey(tenant, id), + cacheCommit(utils.NonTransactional), utils.NonTransactional) + return +} + func (iDB *InternalDB) RemoveLoadIDsDrv() (err error) { return utils.ErrNotImplemented } diff --git a/engine/storage_internal_stordb.go b/engine/storage_internal_stordb.go index e5340a35c..db3789d8f 100644 --- a/engine/storage_internal_stordb.go +++ b/engine/storage_internal_stordb.go @@ -585,6 +585,28 @@ func (iDB *InternalDB) GetTPRateProfiles(tpid, tenant, id string) (tpPrfs []*uti return } +func (iDB *InternalDB) GetTPActionProfiles(tpid, tenant, id string) (tpPrfs []*utils.TPActionProfile, err error) { + key := tpid + if tenant != utils.EmptyString { + key += utils.CONCATENATED_KEY_SEP + tenant + } + if id != utils.EmptyString { + key += utils.CONCATENATED_KEY_SEP + id + } + ids := Cache.GetItemIDs(utils.CacheTBLTPActionProfiles, key) + for _, id := range ids { + x, ok := Cache.Get(utils.CacheTBLTPActionProfiles, id) + if !ok || x == nil { + return nil, utils.ErrNotFound + } + tpPrfs = append(tpPrfs, x.(*utils.TPActionProfile)) + } + if len(tpPrfs) == 0 { + return nil, utils.ErrNotFound + } + return +} + //implement LoadWriter interface func (iDB *InternalDB) RemTpData(table, tpid string, args map[string]string) (err error) { if table == utils.EmptyString { @@ -847,6 +869,17 @@ func (iDB *InternalDB) SetTPRateProfiles(tpPrfs []*utils.TPRateProfile) (err err return } +func (iDB *InternalDB) SetTPActionProfiles(tpPrfs []*utils.TPActionProfile) (err error) { + if len(tpPrfs) == 0 { + return nil + } + for _, tpPrf := range tpPrfs { + Cache.SetWithoutReplicate(utils.CacheTBLTPActionProfiles, utils.ConcatenatedKey(tpPrf.TPid, tpPrf.Tenant, tpPrf.ID), tpPrf, nil, + cacheCommit(utils.NonTransactional), utils.NonTransactional) + } + return +} + //implement CdrStorage interface func (iDB *InternalDB) SetCDR(cdr *CDR, allowUpdate bool) (err error) { if cdr.OrderID == 0 { diff --git a/engine/storage_mongo_datadb.go b/engine/storage_mongo_datadb.go index 631df3560..534a7e821 100644 --- a/engine/storage_mongo_datadb.go +++ b/engine/storage_mongo_datadb.go @@ -75,6 +75,7 @@ const ( ColDpp = "dispatcher_profiles" ColDph = "dispatcher_hosts" ColRpp = "rate_profiles" + ColApp = "action_profiles" ColLID = "load_ids" ) @@ -280,7 +281,7 @@ func (ms *MongoStorage) ensureIndexesForCol(col string) (err error) { // exporte if err = ms.enusureIndex(col, true, "key"); err != nil { return } - case ColRsP, ColRes, ColSqs, ColSqp, ColTps, ColThs, ColRts, ColAttr, ColFlt, ColCpp, ColDpp, ColDph, ColRpp: + case ColRsP, ColRes, ColSqs, ColSqp, ColTps, ColThs, ColRts, ColAttr, ColFlt, ColCpp, ColDpp, ColDph, ColRpp, ColApp: if err = ms.enusureIndex(col, true, "tenant", "id"); err != nil { return } @@ -344,7 +345,7 @@ func (ms *MongoStorage) EnsureIndexes(cols ...string) (err error) { if ms.storageType == utils.DataDB { for _, col := range []string{ColAct, ColApl, ColAAp, ColAtr, ColRpl, ColDst, ColRds, ColLht, ColIndx, ColRsP, ColRes, ColSqs, ColSqp, - ColTps, ColThs, ColRts, ColAttr, ColFlt, ColCpp, ColDpp, ColRpp, + ColTps, ColThs, ColRts, ColAttr, ColFlt, ColCpp, ColDpp, ColRpp, ColApp, ColRpf, ColShg, ColAcc} { if err = ms.ensureIndexesForCol(col); err != nil { return @@ -675,6 +676,8 @@ func (ms *MongoStorage) GetKeysForPrefix(prefix string) (result []string, err er result, err = ms.getField2(sctx, ColDpp, utils.DispatcherProfilePrefix, subject, tntID) case utils.RateProfilePrefix: result, err = ms.getField2(sctx, ColRpp, utils.RateProfilePrefix, subject, tntID) + case utils.ActionProfilePrefix: + result, err = ms.getField2(sctx, ColApp, utils.ActionProfilePrefix, subject, tntID) case utils.DispatcherHostPrefix: result, err = ms.getField2(sctx, ColDph, utils.DispatcherHostPrefix, subject, tntID) case utils.AttributeFilterIndexes: @@ -693,8 +696,8 @@ func (ms *MongoStorage) GetKeysForPrefix(prefix string) (result []string, err er result, err = ms.getField3(sctx, ColIndx, utils.DispatcherFilterIndexes, "key") case utils.ActionPlanIndexes: result, err = ms.getField3(sctx, ColIndx, utils.ActionPlanIndexes, "key") - case utils.RateProfilesFilterIndexPrfx: - result, err = ms.getField3(sctx, ColIndx, utils.RateProfilesFilterIndexPrfx, "key") + case utils.ActionProfilesFilterIndexPrfx: + result, err = ms.getField3(sctx, ColIndx, utils.ActionProfilesFilterIndexPrfx, "key") case utils.RateFilterIndexPrfx: result, err = ms.getField3(sctx, ColIndx, utils.RateFilterIndexPrfx, "key") case utils.FilterIndexPrfx: @@ -749,6 +752,8 @@ func (ms *MongoStorage) HasDataDrv(category, subject, tenant string) (has bool, count, err = ms.getCol(ColDph).CountDocuments(sctx, bson.M{"tenant": tenant, "id": subject}) case utils.RateProfilePrefix: count, err = ms.getCol(ColRpp).CountDocuments(sctx, bson.M{"tenant": tenant, "id": subject}) + case utils.ActionProfilePrefix: + count, err = ms.getCol(ColApp).CountDocuments(sctx, bson.M{"tenant": tenant, "id": subject}) default: err = fmt.Errorf("unsupported category in HasData: %s", category) } @@ -2189,6 +2194,42 @@ func (ms *MongoStorage) RemoveRateProfileDrv(tenant, id string) (err error) { }) } +func (ms *MongoStorage) GetActionProfileDrv(tenant, id string) (ap *ActionProfile, err error) { + ap = new(ActionProfile) + err = ms.query(func(sctx mongo.SessionContext) (err error) { + cur := ms.getCol(ColApp).FindOne(sctx, bson.M{"tenant": tenant, "id": id}) + if err := cur.Decode(ap); err != nil { + ap = nil + if err == mongo.ErrNoDocuments { + return utils.ErrNotFound + } + return err + } + return nil + }) + return +} + +func (ms *MongoStorage) SetActionProfileDrv(ap *ActionProfile) (err error) { + return ms.query(func(sctx mongo.SessionContext) (err error) { + _, err = ms.getCol(ColApp).UpdateOne(sctx, bson.M{"tenant": ap.Tenant, "id": ap.ID}, + bson.M{"$set": ap}, + options.Update().SetUpsert(true), + ) + return err + }) +} + +func (ms *MongoStorage) RemoveActionProfileDrv(tenant, id string) (err error) { + return ms.query(func(sctx mongo.SessionContext) (err error) { + dr, err := ms.getCol(ColApp).DeleteOne(sctx, bson.M{"tenant": tenant, "id": id}) + if dr.DeletedCount == 0 { + return utils.ErrNotFound + } + return err + }) +} + // GetIndexesDrv retrieves Indexes from dataDB // the key is the tenant of the item or in case of context dependent profiles is a concatenatedKey between tenant and context // id is used as a concatenated key in case of filterIndexes the id will be filterType:fieldName:fieldVal diff --git a/engine/storage_mongo_stordb.go b/engine/storage_mongo_stordb.go index 0c31d3a4f..9baa722f5 100644 --- a/engine/storage_mongo_stordb.go +++ b/engine/storage_mongo_stordb.go @@ -1579,6 +1579,54 @@ func (ms *MongoStorage) SetTPRateProfiles(tpDPPs []*utils.TPRateProfile) (err er }) } +func (ms *MongoStorage) GetTPActionProfiles(tpid, tenant, id string) ([]*utils.TPActionProfile, error) { + filter := bson.M{"tpid": tpid} + if id != "" { + filter["id"] = id + } + if tenant != "" { + filter["tenant"] = tenant + } + var results []*utils.TPActionProfile + err := ms.query(func(sctx mongo.SessionContext) (err error) { + cur, err := ms.getCol(utils.TBLTPActionProfiles).Find(sctx, filter) + if err != nil { + return err + } + for cur.Next(sctx) { + var tp utils.TPActionProfile + err := cur.Decode(&tp) + if err != nil { + return err + } + results = append(results, &tp) + } + if len(results) == 0 { + return utils.ErrNotFound + } + return cur.Close(sctx) + }) + return results, err +} + +func (ms *MongoStorage) SetTPActionProfiles(tpAps []*utils.TPActionProfile) (err error) { + if len(tpAps) == 0 { + return + } + return ms.query(func(sctx mongo.SessionContext) (err error) { + for _, tp := range tpAps { + _, err = ms.getCol(utils.TBLTPActionProfiles).UpdateOne(sctx, bson.M{"tpid": tp.TPid, "id": tp.ID}, + bson.M{"$set": tp}, + options.Update().SetUpsert(true), + ) + if err != nil { + return err + } + } + return nil + }) +} + func (ms *MongoStorage) GetVersions(itm string) (vrs Versions, err error) { fop := options.FindOne() if itm != "" { diff --git a/engine/storage_redis.go b/engine/storage_redis.go index b3475a94a..59871d9a5 100644 --- a/engine/storage_redis.go +++ b/engine/storage_redis.go @@ -1449,6 +1449,30 @@ func (rs *RedisStorage) RemoveRateProfileDrv(tenant, id string) (err error) { return rs.Cmd(nil, redis_DEL, utils.RateProfilePrefix+utils.ConcatenatedKey(tenant, id)) } +func (rs *RedisStorage) GetActionProfileDrv(tenant, id string) (ap *ActionProfile, err error) { + var values []byte + if err = rs.Cmd(&values, redis_GET, utils.ActionProfilePrefix+utils.ConcatenatedKey(tenant, id)); err != nil { + return + } else if len(values) == 0 { + err = utils.ErrNotFound + return + } + err = rs.ms.Unmarshal(values, &ap) + return +} + +func (rs *RedisStorage) SetActionProfileDrv(ap *ActionProfile) (err error) { + var result []byte + if result, err = rs.ms.Marshal(ap); err != nil { + return + } + return rs.Cmd(nil, redis_SET, utils.ActionProfilePrefix+utils.ConcatenatedKey(ap.Tenant, ap.ID), string(result)) +} + +func (rs *RedisStorage) RemoveActionProfileDrv(tenant, id string) (err error) { + return rs.Cmd(nil, redis_DEL, utils.ActionProfilePrefix+utils.ConcatenatedKey(tenant, id)) +} + // GetIndexesDrv retrieves Indexes from dataDB func (rs *RedisStorage) GetIndexesDrv(idxItmType, tntCtx, idxKey string) (indexes map[string]utils.StringSet, err error) { mp := make(map[string]string) diff --git a/engine/storage_sql.go b/engine/storage_sql.go index 7e047d99e..078a08f94 100644 --- a/engine/storage_sql.go +++ b/engine/storage_sql.go @@ -747,6 +747,28 @@ func (self *SQLStorage) SetTPRateProfiles(tpDPPs []*utils.TPRateProfile) error { return nil } +func (self *SQLStorage) SetTPActionProfiles(tpAps []*utils.TPActionProfile) error { + if len(tpAps) == 0 { + return nil + } + tx := self.db.Begin() + for _, tpAp := range tpAps { + // Remove previous + if err := tx.Where(&ActionProfileMdl{Tpid: tpAp.TPid, Tenant: tpAp.Tenant, ID: tpAp.ID}).Delete(ActionProfileMdl{}).Error; err != nil { + tx.Rollback() + return err + } + for _, mst := range APItoModelTPActionProfile(tpAp) { + if err := tx.Save(&mst).Error; err != nil { + tx.Rollback() + return err + } + } + } + tx.Commit() + return nil +} + func (self *SQLStorage) SetSMCost(smc *SMCost) error { if smc.CostDetails == nil { return nil @@ -1611,6 +1633,25 @@ func (self *SQLStorage) GetTPRateProfiles(tpid, tenant, id string) ([]*utils.TPR return arls, nil } +func (self *SQLStorage) GetTPActionProfiles(tpid, tenant, id string) ([]*utils.TPActionProfile, error) { + var dpps ActionProfileMdls + q := self.db.Where("tpid = ?", tpid) + if len(id) != 0 { + q = q.Where("id = ?", id) + } + if len(tenant) != 0 { + q = q.Where("tenant = ?", tenant) + } + if err := q.Find(&dpps).Error; err != nil { + return nil, err + } + arls := dpps.AsTPActionProfile() + if len(arls) == 0 { + return arls, utils.ErrNotFound + } + return arls, nil +} + // GetVersions returns slice of all versions or a specific version if tag is specified func (self *SQLStorage) GetVersions(itm string) (vrs Versions, err error) { q := self.db.Model(&TBLVersion{}) diff --git a/engine/tpreader.go b/engine/tpreader.go index d5f524af2..39ea65c45 100644 --- a/engine/tpreader.go +++ b/engine/tpreader.go @@ -57,6 +57,7 @@ type TpReader struct { dispatcherProfiles map[utils.TenantID]*utils.TPDispatcherProfile dispatcherHosts map[utils.TenantID]*utils.TPDispatcherHost rateProfiles map[utils.TenantID]*utils.TPRateProfile + actionProfiles map[utils.TenantID]*utils.TPActionProfile resources []*utils.TenantID // IDs of resources which need creation based on resourceProfiles statQueues []*utils.TenantID // IDs of statQueues which need creation based on statQueueProfiles thresholds []*utils.TenantID // IDs of thresholds which need creation based on thresholdProfiles @@ -107,6 +108,7 @@ func (tpr *TpReader) Init() { tpr.dispatcherProfiles = make(map[utils.TenantID]*utils.TPDispatcherProfile) tpr.dispatcherHosts = make(map[utils.TenantID]*utils.TPDispatcherHost) tpr.rateProfiles = make(map[utils.TenantID]*utils.TPRateProfile) + tpr.actionProfiles = make(map[utils.TenantID]*utils.TPActionProfile) tpr.filters = make(map[utils.TenantID]*utils.TPFilterProfile) tpr.revDests = make(map[string][]string) tpr.acntActionPlans = make(map[string][]string) @@ -1296,6 +1298,26 @@ func (tpr *TpReader) LoadRateProfilesFiltered(tag string) (err error) { return nil } +func (tpr *TpReader) LoadActionProfiles() error { + return tpr.LoadActionProfilesFiltered("") +} + +func (tpr *TpReader) LoadActionProfilesFiltered(tag string) (err error) { + aps, err := tpr.lr.GetTPActionProfiles(tpr.tpid, "", tag) + if err != nil { + return err + } + mapActionProfiles := make(map[utils.TenantID]*utils.TPActionProfile) + for _, ap := range aps { + if err = verifyInlineFilterS(ap.FilterIDs); err != nil { + return + } + mapActionProfiles[utils.TenantID{Tenant: ap.Tenant, ID: ap.ID}] = ap + } + tpr.actionProfiles = mapActionProfiles + return nil +} + func (tpr *TpReader) LoadDispatcherHosts() error { return tpr.LoadDispatcherHostsFiltered("") } @@ -1364,6 +1386,9 @@ func (tpr *TpReader) LoadAll() (err error) { if err = tpr.LoadRateProfiles(); err != nil && err.Error() != utils.NotFoundCaps { return } + if err = tpr.LoadActionProfiles(); err != nil && err.Error() != utils.NotFoundCaps { + return + } return nil } @@ -1813,6 +1838,25 @@ func (tpr *TpReader) WriteToDatabase(verbose, disable_reverse bool) (err error) loadIDs[utils.CacheRateProfiles] = loadID } + if verbose { + log.Print("ActionProfiles:") + } + for _, tpAP := range tpr.actionProfiles { + var ap *ActionProfile + if ap, err = APItoActionProfile(tpAP, tpr.timezone); err != nil { + return + } + if err = tpr.dm.SetActionProfile(ap, true); err != nil { + return + } + if verbose { + log.Print("\t", ap.TenantID()) + } + } + if len(tpr.actionProfiles) != 0 { + loadIDs[utils.CacheActionProfiles] = loadID + } + if verbose { log.Print("Timings:") } @@ -1919,6 +1963,8 @@ func (tpr *TpReader) ShowStatistics() { log.Print("DispatcherHosts: ", len(tpr.dispatcherHosts)) // Rate profiles log.Print("RateProfiles: ", len(tpr.rateProfiles)) + // Action profiles + log.Print("ActionProfiles: ", len(tpr.actionProfiles)) } // GetLoadedIds returns the identities loaded for a specific category, useful for cache reloads @@ -2078,6 +2124,14 @@ func (tpr *TpReader) GetLoadedIds(categ string) ([]string, error) { i++ } return keys, nil + case utils.ActionProfilePrefix: + keys := make([]string, len(tpr.actionProfiles)) + i := 0 + for k := range tpr.actionProfiles { + keys[i] = k.TenantID() + i++ + } + return keys, nil } return nil, errors.New("Unsupported load category") } @@ -2321,6 +2375,19 @@ func (tpr *TpReader) RemoveFromDatabase(verbose, disable_reverse bool) (err erro } } + if verbose { + log.Print("ActionProfiles:") + } + for _, tpAp := range tpr.actionProfiles { + if err = tpr.dm.RemoveActionProfile(tpAp.Tenant, tpAp.ID, + utils.NonTransactional, true); err != nil { + return + } + if verbose { + log.Print("\t", utils.ConcatenatedKey(tpAp.Tenant, tpAp.ID)) + } + } + if verbose { log.Print("Timings:") } @@ -2429,6 +2496,9 @@ func (tpr *TpReader) RemoveFromDatabase(verbose, disable_reverse bool) (err erro if len(tpr.rateProfiles) != 0 { loadIDs[utils.CacheRateProfiles] = loadID } + if len(tpr.actionProfiles) != 0 { + loadIDs[utils.CacheActionProfiles] = loadID + } if len(tpr.timings) != 0 { loadIDs[utils.CacheTimings] = loadID } @@ -2465,6 +2535,7 @@ func (tpr *TpReader) ReloadCache(caching string, verbose bool, opts map[string]i dppIDs, _ := tpr.GetLoadedIds(utils.DispatcherProfilePrefix) dphIDs, _ := tpr.GetLoadedIds(utils.DispatcherHostPrefix) ratePrfIDs, _ := tpr.GetLoadedIds(utils.RateProfilePrefix) + actionPrfIDs, _ := tpr.GetLoadedIds(utils.ActionProfilePrefix) aps, _ := tpr.GetLoadedIds(utils.ACTION_PLAN_PREFIX) //compose Reload Cache argument @@ -2492,7 +2563,7 @@ func (tpr *TpReader) ReloadCache(caching string, verbose bool, opts map[string]i utils.ChargerProfileIDs: chargerIDs, utils.DispatcherProfileIDs: dppIDs, utils.DispatcherHostIDs: dphIDs, - utils.RateProfileIDs: ratePrfIDs, + utils.ActionProfileIDs: actionPrfIDs, }, } @@ -2548,6 +2619,9 @@ func (tpr *TpReader) ReloadCache(caching string, verbose bool, opts map[string]i cacheIDs = append(cacheIDs, utils.CacheRateProfilesFilterIndexes) cacheIDs = append(cacheIDs, utils.CacheRateFilterIndexes) } + if len(actionPrfIDs) != 0 { + cacheIDs = append(cacheIDs, utils.CacheActionProfilesFilterIndexes) + } if len(flrIDs) != 0 { cacheIDs = append(cacheIDs, utils.CacheReverseFilterIndexes) } diff --git a/general_tests/acntacts_test.go b/general_tests/acntacts_test.go index a9668e0da..a8ea761a0 100644 --- a/general_tests/acntacts_test.go +++ b/general_tests/acntacts_test.go @@ -58,7 +58,7 @@ ENABLE_ACNT,*enable_account,,,,,,,,,,,,,false,false,10` csvr, err := engine.NewTpReader(dbAcntActs.DataDB(), engine.NewStringCSVStorage(utils.CSV_SEP, destinations, timings, rates, destinationRates, ratingPlans, ratingProfiles, sharedGroups, actions, actionPlans, actionTriggers, accountActions, - resLimits, stats, thresholds, filters, suppliers, attrProfiles, chargerProfiles, ``, "", utils.EmptyString), "", "", nil, nil, false) + resLimits, stats, thresholds, filters, suppliers, attrProfiles, chargerProfiles, ``, "", utils.EmptyString, utils.EmptyString), "", "", nil, nil, false) if err != nil { t.Error(err) } diff --git a/general_tests/auth_test.go b/general_tests/auth_test.go index d24c9c1e7..b95d81767 100644 --- a/general_tests/auth_test.go +++ b/general_tests/auth_test.go @@ -60,7 +60,7 @@ func TestAuthLoadCsvError(t *testing.T) { chargerProfiles := `` csvr, err := engine.NewTpReader(dbAuth.DataDB(), engine.NewStringCSVStorage(utils.CSV_SEP, destinations, timings, rates, destinationRates, ratingPlans, ratingProfiles, sharedGroups, actions, actionPlans, actionTriggers, accountActions, - resLimits, stats, thresholds, filters, suppliers, attrProfiles, chargerProfiles, ``, "", utils.EmptyString), "", "", nil, nil, false) + resLimits, stats, thresholds, filters, suppliers, attrProfiles, chargerProfiles, ``, "", utils.EmptyString, utils.EmptyString), "", "", nil, nil, false) if err != nil { t.Error(err) } @@ -94,7 +94,7 @@ cgrates.org,call,*any,2013-01-06T00:00:00Z,RP_ANY,` chargerProfiles := `` csvr, err := engine.NewTpReader(dbAuth.DataDB(), engine.NewStringCSVStorage(utils.CSV_SEP, destinations, timings, rates, destinationRates, ratingPlans, ratingProfiles, sharedGroups, actions, actionPlans, actionTriggers, accountActions, - resLimits, stats, thresholds, filters, suppliers, attrProfiles, chargerProfiles, ``, "", utils.EmptyString), "", "", nil, nil, false) + resLimits, stats, thresholds, filters, suppliers, attrProfiles, chargerProfiles, ``, "", utils.EmptyString, utils.EmptyString), "", "", nil, nil, false) if err != nil { t.Error(err) } diff --git a/general_tests/costs1_test.go b/general_tests/costs1_test.go index acf3da219..0a62ed97c 100644 --- a/general_tests/costs1_test.go +++ b/general_tests/costs1_test.go @@ -59,7 +59,7 @@ cgrates.org,sms,*any,2012-01-01T00:00:00Z,RP_SMS1,` utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, - utils.EmptyString, utils.EmptyString, utils.EmptyString), + utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString), utils.EmptyString, utils.EmptyString, nil, nil, false) if err != nil { t.Error(err) diff --git a/general_tests/datachrg1_test.go b/general_tests/datachrg1_test.go index 52ef1b5a8..084a9dd0c 100644 --- a/general_tests/datachrg1_test.go +++ b/general_tests/datachrg1_test.go @@ -47,7 +47,7 @@ RP_DATA1,DR_DATA_2,TM2,10` utils.EmptyString, timings, rates, destinationRates, ratingPlans, ratingProfiles, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, - utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString), + utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString), utils.EmptyString, utils.EmptyString, nil, nil, false) if err != nil { t.Error(err) diff --git a/general_tests/ddazmbl1_test.go b/general_tests/ddazmbl1_test.go index df939aa65..e3be91e53 100644 --- a/general_tests/ddazmbl1_test.go +++ b/general_tests/ddazmbl1_test.go @@ -68,7 +68,7 @@ TOPUP10_AT,TOPUP10_AC1,ASAP,10` destinationRates, ratingPlans, ratingProfiles, sharedGroups, actions, actionPlans, actionTriggers, accountActions, resLimits, stats, thresholds, filters, suppliers, - attrProfiles, chargerProfiles, ``, "", utils.EmptyString), "", "", nil, nil, false) + attrProfiles, chargerProfiles, ``, "", utils.EmptyString, utils.EmptyString), "", "", nil, nil, false) if err != nil { t.Error(err) } diff --git a/general_tests/ddazmbl2_test.go b/general_tests/ddazmbl2_test.go index 155c7aa04..235b0b099 100644 --- a/general_tests/ddazmbl2_test.go +++ b/general_tests/ddazmbl2_test.go @@ -66,7 +66,7 @@ TOPUP10_AT,TOPUP10_AC1,ASAP,10` csvr, err := engine.NewTpReader(dataDB2.DataDB(), engine.NewStringCSVStorage(utils.CSV_SEP, destinations, timings, rates, destinationRates, ratingPlans, ratingProfiles, sharedGroups, actions, actionPlans, actionTriggers, accountActions, resLimits, - stats, thresholds, filters, suppliers, attrProfiles, chargerProfiles, ``, "", utils.EmptyString), "", "", nil, nil, false) + stats, thresholds, filters, suppliers, attrProfiles, chargerProfiles, ``, "", utils.EmptyString, utils.EmptyString), "", "", nil, nil, false) if err != nil { t.Error(err) } diff --git a/general_tests/ddazmbl3_test.go b/general_tests/ddazmbl3_test.go index 22957c2e6..926538e74 100644 --- a/general_tests/ddazmbl3_test.go +++ b/general_tests/ddazmbl3_test.go @@ -64,7 +64,7 @@ cgrates.org,call,discounted_minutes,2013-01-06T00:00:00Z,RP_UK_Mobile_BIG5_PKG,` csvr, err := engine.NewTpReader(dataDB3.DataDB(), engine.NewStringCSVStorage(utils.CSV_SEP, destinations, timings, rates, destinationRates, ratingPlans, ratingProfiles, sharedGroups, actions, actionPlans, actionTriggers, accountActions, resLimits, stats, - thresholds, filters, suppliers, attrProfiles, chargerProfiles, ``, "", utils.EmptyString), "", "", nil, nil, false) + thresholds, filters, suppliers, attrProfiles, chargerProfiles, ``, "", utils.EmptyString, utils.EmptyString), "", "", nil, nil, false) if err != nil { t.Error(err) } diff --git a/general_tests/smschrg1_test.go b/general_tests/smschrg1_test.go index 633bea53e..c14e78e58 100644 --- a/general_tests/smschrg1_test.go +++ b/general_tests/smschrg1_test.go @@ -47,7 +47,7 @@ func TestSMSLoadCsvTpSmsChrg1(t *testing.T) { utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, - utils.EmptyString, utils.EmptyString, utils.EmptyString), utils.EmptyString, + utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString), utils.EmptyString, utils.EmptyString, nil, nil, false) if err != nil { t.Error(err) diff --git a/utils/consts.go b/utils/consts.go index 726358b2e..01aa5d9b4 100755 --- a/utils/consts.go +++ b/utils/consts.go @@ -369,6 +369,7 @@ const ( ChargerProfilePrefix = "cpp_" DispatcherProfilePrefix = "dpp_" RateProfilePrefix = "rtp_" + ActionProfilePrefix = "acp_" DispatcherHostPrefix = "dph_" ThresholdProfilePrefix = "thp_" StatQueuePrefix = "stq_" @@ -526,6 +527,7 @@ const ( DispatcherProfiles = "DispatcherProfiles" DispatcherHosts = "DispatcherHosts" RateProfiles = "RateProfiles" + ActionProfiles = "ActionProfiles" MetaEveryMinute = "*every_minute" MetaHourly = "*hourly" ID = "ID" @@ -918,6 +920,7 @@ const ( MetaThresholds = "*thresholds" MetaRoutes = "*routes" MetaAttributes = "*attributes" + MetaActionProfiles = "*action_profiles" MetaLoadIDs = "*load_ids" ) @@ -1166,6 +1169,7 @@ const ( ReplicatorSv1GetChargerProfile = "ReplicatorSv1.GetChargerProfile" ReplicatorSv1GetDispatcherProfile = "ReplicatorSv1.GetDispatcherProfile" ReplicatorSv1GetRateProfile = "ReplicatorSv1.GetRateProfile" + ReplicatorSv1GetActionProfile = "ReplicatorSv1.GetActionProfile" ReplicatorSv1GetDispatcherHost = "ReplicatorSv1.GetDispatcherHost" ReplicatorSv1GetItemLoadIDs = "ReplicatorSv1.GetItemLoadIDs" ReplicatorSv1SetThresholdProfile = "ReplicatorSv1.SetThresholdProfile" @@ -1191,6 +1195,7 @@ const ( ReplicatorSv1SetChargerProfile = "ReplicatorSv1.SetChargerProfile" ReplicatorSv1SetDispatcherProfile = "ReplicatorSv1.SetDispatcherProfile" ReplicatorSv1SetRateProfile = "ReplicatorSv1.SetRateProfile" + ReplicatorSv1SetActionProfile = "ReplicatorSv1.SetActionProfile" ReplicatorSv1SetDispatcherHost = "ReplicatorSv1.SetDispatcherHost" ReplicatorSv1SetLoadIDs = "ReplicatorSv1.SetLoadIDs" ReplicatorSv1RemoveThreshold = "ReplicatorSv1.RemoveThreshold" @@ -1215,6 +1220,7 @@ const ( ReplicatorSv1RemoveChargerProfile = "ReplicatorSv1.RemoveChargerProfile" ReplicatorSv1RemoveDispatcherProfile = "ReplicatorSv1.RemoveDispatcherProfile" ReplicatorSv1RemoveRateProfile = "ReplicatorSv1.RemoveRateProfile" + ReplicatorSv1RemoveActionProfile = "ReplicatorSv1.RemoveActionProfile" ReplicatorSv1RemoveDispatcherHost = "ReplicatorSv1.RemoveDispatcherHost" ReplicatorSv1GetIndexes = "ReplicatorSv1.GetIndexes" ReplicatorSv1SetIndexes = "ReplicatorSv1.SetIndexes" @@ -1740,6 +1746,7 @@ const ( DispatcherProfilesCsv = "DispatcherProfiles.csv" DispatcherHostsCsv = "DispatcherHosts.csv" RateProfilesCsv = "RateProfiles.csv" + ActionProfilesCsv = "ActionProfiles.csv" ) // Table Name @@ -1774,58 +1781,60 @@ const ( // Cache Name const ( - CacheDestinations = "*destinations" - CacheReverseDestinations = "*reverse_destinations" - CacheRatingPlans = "*rating_plans" - CacheRatingProfiles = "*rating_profiles" - CacheActions = "*actions" - CacheActionPlans = "*action_plans" - CacheAccountActionPlans = "*account_action_plans" - CacheActionTriggers = "*action_triggers" - CacheSharedGroups = "*shared_groups" - CacheResources = "*resources" - CacheResourceProfiles = "*resource_profiles" - CacheTimings = "*timings" - CacheEventResources = "*event_resources" - CacheStatQueueProfiles = "*statqueue_profiles" - CacheStatQueues = "*statqueues" - CacheThresholdProfiles = "*threshold_profiles" - CacheThresholds = "*thresholds" - CacheFilters = "*filters" - CacheRouteProfiles = "*route_profiles" - CacheAttributeProfiles = "*attribute_profiles" - CacheChargerProfiles = "*charger_profiles" - CacheDispatcherProfiles = "*dispatcher_profiles" - CacheDispatcherHosts = "*dispatcher_hosts" - CacheDispatchers = "*dispatchers" - CacheDispatcherRoutes = "*dispatcher_routes" - CacheDispatcherLoads = "*dispatcher_loads" - CacheRateProfiles = "*rate_profiles" - CacheResourceFilterIndexes = "*resource_filter_indexes" - CacheStatFilterIndexes = "*stat_filter_indexes" - CacheThresholdFilterIndexes = "*threshold_filter_indexes" - CacheRouteFilterIndexes = "*route_filter_indexes" - CacheAttributeFilterIndexes = "*attribute_filter_indexes" - CacheChargerFilterIndexes = "*charger_filter_indexes" - CacheDispatcherFilterIndexes = "*dispatcher_filter_indexes" - CacheDiameterMessages = "*diameter_messages" - CacheRPCResponses = "*rpc_responses" - CacheClosedSessions = "*closed_sessions" - CacheRateProfilesFilterIndexes = "*rate_profile_filter_indexes" - CacheRateFilterIndexes = "*rate_filter_indexes" - MetaPrecaching = "*precaching" - MetaReady = "*ready" - CacheLoadIDs = "*load_ids" - CacheRPCConnections = "*rpc_connections" - CacheCDRIDs = "*cdr_ids" - CacheRatingProfilesTmp = "*tmp_rating_profiles" - CacheUCH = "*uch" - CacheSTIR = "*stir" - CacheEventCharges = "*event_charges" - CacheReverseFilterIndexes = "*reverse_filter_indexes" - CacheAccounts = "*accounts" - CacheVersions = "*versions" - CacheCapsEvents = "*caps_events" + CacheDestinations = "*destinations" + CacheReverseDestinations = "*reverse_destinations" + CacheRatingPlans = "*rating_plans" + CacheRatingProfiles = "*rating_profiles" + CacheActions = "*actions" + CacheActionPlans = "*action_plans" + CacheAccountActionPlans = "*account_action_plans" + CacheActionTriggers = "*action_triggers" + CacheSharedGroups = "*shared_groups" + CacheResources = "*resources" + CacheResourceProfiles = "*resource_profiles" + CacheTimings = "*timings" + CacheEventResources = "*event_resources" + CacheStatQueueProfiles = "*statqueue_profiles" + CacheStatQueues = "*statqueues" + CacheThresholdProfiles = "*threshold_profiles" + CacheThresholds = "*thresholds" + CacheFilters = "*filters" + CacheRouteProfiles = "*route_profiles" + CacheAttributeProfiles = "*attribute_profiles" + CacheChargerProfiles = "*charger_profiles" + CacheDispatcherProfiles = "*dispatcher_profiles" + CacheDispatcherHosts = "*dispatcher_hosts" + CacheDispatchers = "*dispatchers" + CacheDispatcherRoutes = "*dispatcher_routes" + CacheDispatcherLoads = "*dispatcher_loads" + CacheRateProfiles = "*rate_profiles" + CacheActionProfiles = "*action_profiles" + CacheResourceFilterIndexes = "*resource_filter_indexes" + CacheStatFilterIndexes = "*stat_filter_indexes" + CacheThresholdFilterIndexes = "*threshold_filter_indexes" + CacheRouteFilterIndexes = "*route_filter_indexes" + CacheAttributeFilterIndexes = "*attribute_filter_indexes" + CacheChargerFilterIndexes = "*charger_filter_indexes" + CacheDispatcherFilterIndexes = "*dispatcher_filter_indexes" + CacheDiameterMessages = "*diameter_messages" + CacheRPCResponses = "*rpc_responses" + CacheClosedSessions = "*closed_sessions" + CacheRateProfilesFilterIndexes = "*rate_profile_filter_indexes" + CacheActionProfilesFilterIndexes = "*action_profile_filter_indexes" + CacheRateFilterIndexes = "*rate_filter_indexes" + MetaPrecaching = "*precaching" + MetaReady = "*ready" + CacheLoadIDs = "*load_ids" + CacheRPCConnections = "*rpc_connections" + CacheCDRIDs = "*cdr_ids" + CacheRatingProfilesTmp = "*tmp_rating_profiles" + CacheUCH = "*uch" + CacheSTIR = "*stir" + CacheEventCharges = "*event_charges" + CacheReverseFilterIndexes = "*reverse_filter_indexes" + CacheAccounts = "*accounts" + CacheVersions = "*versions" + CacheCapsEvents = "*caps_events" // storDB CacheTBLTPTimings = "*tp_timings" @@ -1851,21 +1860,23 @@ const ( CacheTBLTPDispatchers = "*tp_dispatcher_profiles" CacheTBLTPDispatcherHosts = "*tp_dispatcher_hosts" CacheTBLTPRateProfiles = "*tp_rate_profiles" + CacheTBLTPActionProfiles = "*tp_action_profiles" ) // Prefix for indexing const ( - ResourceFilterIndexes = "rfi_" - StatFilterIndexes = "sfi_" - ThresholdFilterIndexes = "tfi_" - AttributeFilterIndexes = "afi_" - ChargerFilterIndexes = "cfi_" - DispatcherFilterIndexes = "dfi_" - ActionPlanIndexes = "api_" - RouteFilterIndexes = "rti_" - RateProfilesFilterIndexPrfx = "rpi_" - RateFilterIndexPrfx = "rri_" - FilterIndexPrfx = "fii_" + ResourceFilterIndexes = "rfi_" + StatFilterIndexes = "sfi_" + ThresholdFilterIndexes = "tfi_" + AttributeFilterIndexes = "afi_" + ChargerFilterIndexes = "cfi_" + DispatcherFilterIndexes = "dfi_" + ActionPlanIndexes = "api_" + RouteFilterIndexes = "rti_" + RateProfilesFilterIndexPrfx = "rpi_" + RateFilterIndexPrfx = "rri_" + ActionProfilesFilterIndexPrfx = "aci_" + FilterIndexPrfx = "fii_" ) // Agents @@ -2468,6 +2479,7 @@ const ( DispatcherHostIDs = "DispatcherHostIDs" DispatcherRoutesIDs = "DispatcherRoutesIDs" RateProfileIDs = "RateProfileIDs" + ActionProfileIDs = "ActionProfileIDs" TimingIDs = "TimingIDs" AttributeFilterIndexIDs = "AttributeFilterIndexIDs" ResourceFilterIndexIDs = "ResourceFilterIndexIDs"