diff --git a/engine/version.go b/engine/version.go
index 04df8f59f..bd3421f65 100644
--- a/engine/version.go
+++ b/engine/version.go
@@ -165,6 +165,7 @@ func CurrentDataDBVersions() Versions {
utils.Dispatchers: 2,
utils.LoadIDsVrs: 1,
utils.RateProfiles: 1,
+ utils.ActionProfiles: 1,
}
}
@@ -196,6 +197,7 @@ func CurrentStorDBVersions() Versions {
utils.TpChargers: 1,
utils.TpDispatchers: 1,
utils.TpRateProfiles: 1,
+ utils.TpActionProfiles: 1,
}
}
diff --git a/migrator/action_profiles.go b/migrator/action_profiles.go
new file mode 100644
index 000000000..39dee5333
--- /dev/null
+++ b/migrator/action_profiles.go
@@ -0,0 +1,95 @@
+/*
+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 migrator
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/cgrates/cgrates/engine"
+
+ "github.com/cgrates/cgrates/utils"
+)
+
+func (m *Migrator) migrateCurrentActionProfiles() (err error) {
+ var ids []string
+ ids, err = m.dmIN.DataManager().DataDB().GetKeysForPrefix(utils.ActionProfilePrefix)
+ if err != nil {
+ return err
+ }
+ for _, id := range ids {
+ tntID := strings.SplitN(strings.TrimPrefix(id, utils.ActionProfilePrefix), utils.InInFieldSep, 2)
+ if len(tntID) < 2 {
+ return fmt.Errorf("Invalid key <%s> when migrating from action profiles", id)
+ }
+ ap, err := m.dmIN.DataManager().GetActionProfile(tntID[0], tntID[1], false, false, utils.NonTransactional)
+ if err != nil {
+ return err
+ }
+ if ap == nil || m.dryRun {
+ continue
+ }
+ if err := m.dmOut.DataManager().SetActionProfile(ap, true); err != nil {
+ return err
+ }
+ if err := m.dmIN.DataManager().RemoveActionProfile(tntID[0], tntID[1], utils.NonTransactional, false); err != nil {
+ return err
+ }
+ m.stats[utils.ActionProfiles]++
+ }
+ return
+}
+
+func (m *Migrator) migrateActionProfiles() (err error) {
+ var vrs engine.Versions
+ current := engine.CurrentDataDBVersions()
+ if vrs, err = m.getVersions(utils.ActionProfiles); err != nil {
+ return
+ }
+ migrated := true
+ for {
+ version := vrs[utils.ActionProfiles]
+ for {
+ switch version {
+ default:
+ return fmt.Errorf("Unsupported version %v", version)
+ case current[utils.ActionProfiles]:
+ migrated = false
+ if m.sameDataDB {
+ break
+ }
+ if err = m.migrateCurrentActionProfiles(); err != nil {
+ return
+ }
+ }
+ if version == current[utils.ActionProfiles] || err == utils.ErrNoMoreData {
+ break
+ }
+ }
+ if err == utils.ErrNoMoreData || !migrated {
+ break
+ }
+ m.stats[utils.ActionProfiles]++
+ }
+ //All done, update version with current one
+ if err = m.setVersions(utils.ActionProfiles); err != nil {
+ return
+ }
+ return m.ensureIndexesDataDB(engine.ColApp)
+}
diff --git a/migrator/actionprofiles_it_test.go b/migrator/actionprofiles_it_test.go
new file mode 100644
index 000000000..0c430cd40
--- /dev/null
+++ b/migrator/actionprofiles_it_test.go
@@ -0,0 +1,252 @@
+// +build integration
+
+/*
+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 migrator
+
+import (
+ "log"
+ "path"
+ "reflect"
+ "testing"
+
+ "github.com/cgrates/cgrates/engine"
+
+ "github.com/cgrates/cgrates/utils"
+
+ "github.com/cgrates/cgrates/config"
+)
+
+var (
+ actPrfPathIn string
+ actPrfPathOut string
+ actPrfCfgIn *config.CGRConfig
+ actPrfCfgOut *config.CGRConfig
+ actPrfMigrator *Migrator
+ actPrfAction string
+)
+
+var sTestsActPrfIT = []func(t *testing.T){
+ testActPrfITConnect,
+ testActPrfITFlush,
+ testActPrfMigrateAndMove,
+}
+
+func TestActPrfITMove1(t *testing.T) {
+ var err error
+ actPrfPathIn = path.Join(*dataDir, "conf", "samples", "tutmongo")
+ actPrfCfgIn, err = config.NewCGRConfigFromPath(actPrfPathIn)
+ if err != nil {
+ t.Fatal(err)
+ }
+ actPrfPathOut = path.Join(*dataDir, "conf", "samples", "tutmysql")
+ actPrfCfgOut, err = config.NewCGRConfigFromPath(actPrfPathOut)
+ if err != nil {
+ t.Fatal(err)
+ }
+ actPrfAction = utils.Move
+ for _, stest := range sTestsActPrfIT {
+ t.Run("TestActPrfITMove1", stest)
+ }
+ actPrfMigrator.Close()
+}
+
+func TestActPrfITMove2(t *testing.T) {
+ var err error
+ actPrfPathIn = path.Join(*dataDir, "conf", "samples", "tutmysql")
+ actPrfCfgIn, err = config.NewCGRConfigFromPath(actPrfPathIn)
+ if err != nil {
+ t.Error(err)
+ }
+ actPrfPathOut = path.Join(*dataDir, "conf", "samples", "tutmongo")
+ actPrfCfgOut, err = config.NewCGRConfigFromPath(actPrfPathOut)
+ if err != nil {
+ t.Error(err)
+ }
+ actPrfAction = utils.Move
+ for _, stest := range sTestsActPrfIT {
+ t.Run("TestActPrfITMove2", stest)
+ }
+ actPrfMigrator.Close()
+}
+
+func TestActPrfITMoveEncoding(t *testing.T) {
+ var err error
+ actPrfPathIn = path.Join(*dataDir, "conf", "samples", "tutmongo")
+ actPrfCfgIn, err = config.NewCGRConfigFromPath(actPrfPathIn)
+ if err != nil {
+ t.Error(err)
+ }
+ actPrfPathOut = path.Join(*dataDir, "conf", "samples", "tutmongojson")
+ actPrfCfgOut, err = config.NewCGRConfigFromPath(actPrfPathOut)
+ if err != nil {
+ t.Error(err)
+ }
+ actPrfAction = utils.Move
+ for _, stest := range sTestsActPrfIT {
+ t.Run("TestActPrfITMove2", stest)
+ }
+ actPrfMigrator.Close()
+}
+
+func TestActPrfITMoveEncoding2(t *testing.T) {
+ var err error
+ actPrfPathIn = path.Join(*dataDir, "conf", "samples", "tutmysql")
+ actPrfCfgIn, err = config.NewCGRConfigFromPath(actPrfPathIn)
+ if err != nil {
+ t.Error(err)
+ }
+ actPrfPathOut = path.Join(*dataDir, "conf", "samples", "tutmysqljson")
+ actPrfCfgOut, err = config.NewCGRConfigFromPath(actPrfPathOut)
+ if err != nil {
+ t.Error(err)
+ }
+ actPrfAction = utils.Move
+ for _, stest := range sTestsActPrfIT {
+ t.Run("TestActPrfITMove2", stest)
+ }
+ actPrfMigrator.Close()
+}
+
+func testActPrfITConnect(t *testing.T) {
+ dataDBIn, err := NewMigratorDataDB(actPrfCfgIn.DataDbCfg().DataDbType,
+ actPrfCfgIn.DataDbCfg().DataDbHost, actPrfCfgIn.DataDbCfg().DataDbPort,
+ actPrfCfgIn.DataDbCfg().DataDbName, actPrfCfgIn.DataDbCfg().DataDbUser,
+ actPrfCfgIn.DataDbCfg().DataDbPass, actPrfCfgIn.GeneralCfg().DBDataEncoding,
+ config.CgrConfig().CacheCfg(), actPrfCfgIn.DataDbCfg().Opts)
+ if err != nil {
+ log.Fatal(err)
+ }
+ dataDBOut, err := NewMigratorDataDB(actPrfCfgOut.DataDbCfg().DataDbType,
+ actPrfCfgOut.DataDbCfg().DataDbHost, actPrfCfgOut.DataDbCfg().DataDbPort,
+ actPrfCfgOut.DataDbCfg().DataDbName, actPrfCfgOut.DataDbCfg().DataDbUser,
+ actPrfCfgOut.DataDbCfg().DataDbPass, actPrfCfgOut.GeneralCfg().DBDataEncoding,
+ config.CgrConfig().CacheCfg(), actPrfCfgOut.DataDbCfg().Opts)
+ if err != nil {
+ log.Fatal(err)
+ }
+ if reflect.DeepEqual(actPrfPathIn, actPrfPathOut) {
+ actPrfMigrator, err = NewMigrator(dataDBIn, dataDBOut, nil, nil,
+ false, true, false, false)
+ } else {
+ actPrfMigrator, err = NewMigrator(dataDBIn, dataDBOut, nil, nil,
+ false, false, false, false)
+ }
+ if err != nil {
+ log.Fatal(err)
+ }
+}
+
+func testActPrfITFlush(t *testing.T) {
+ //dmIn
+ if err := actPrfMigrator.dmIN.DataManager().DataDB().Flush(utils.EmptyString); err != nil {
+ t.Error(err)
+ }
+ if isEmpty, err := actPrfMigrator.dmIN.DataManager().DataDB().IsDBEmpty(); err != nil {
+ t.Error(err)
+ } else if !isEmpty {
+ t.Errorf("Expecting: true got :%+v", isEmpty)
+ }
+ if err := engine.SetDBVersions(actPrfMigrator.dmIN.DataManager().DataDB()); err != nil {
+ t.Error(err)
+ }
+
+ //dmOut
+ if err := actPrfMigrator.dmOut.DataManager().DataDB().Flush(utils.EmptyString); err != nil {
+ t.Error(err)
+ }
+ if isEMpty, err := actPrfMigrator.dmOut.DataManager().DataDB().IsDBEmpty(); err != nil {
+ t.Error(err)
+ } else if !isEMpty {
+ t.Error(err)
+ }
+ if err := engine.SetDBVersions(actPrfMigrator.dmOut.DataManager().DataDB()); err != nil {
+ t.Error(err)
+ }
+}
+
+func testActPrfMigrateAndMove(t *testing.T) {
+ actPrf := &engine.ActionProfile{
+ Tenant: "cgrates.org",
+ ID: "TEST_ID1",
+ FilterIDs: []string{"*string:~*req.Account:1001"},
+ Weight: 20,
+ Schedule: utils.ASAP,
+ AccountIDs: map[string]struct{}{
+ "1001": {},
+ },
+ Actions: []*engine.APAction{
+ {
+ ID: "TOPUP",
+ FilterIDs: []string{},
+ Type: "*topup",
+ Path: "~*balance.TestBalance.Value",
+ },
+ {
+ ID: "TOPUP_TEST_VOICE",
+ FilterIDs: []string{},
+ Type: "*topup",
+ Path: "~*balance.TestVoiceBalance.Value",
+ },
+ },
+ }
+ switch actPrfAction {
+ case utils.Migrate: // for the moment only one version of actions profiles exists
+ case utils.Move:
+ //set, get and migrate
+ if err := actPrfMigrator.dmIN.DataManager().SetActionProfile(actPrf, true); err != nil {
+ t.Error(err)
+ }
+ currentVersion := engine.CurrentDataDBVersions()
+ err := actPrfMigrator.dmIN.DataManager().DataDB().SetVersions(currentVersion, false)
+ if err != nil {
+ t.Error("Error when setting version for ActionPrf", err.Error())
+ }
+
+ _, err = actPrfMigrator.dmOut.DataManager().GetActionProfile(actPrf.Tenant, actPrf.ID,
+ false, false, utils.NonTransactional)
+ if err != utils.ErrNotFound {
+ t.Error(err)
+ }
+
+ err, _ = actPrfMigrator.Migrate([]string{utils.MetaActionProfiles})
+ if err != nil {
+ t.Error("Error when migrating ActPrf", err.Error())
+ }
+ //compared with dmOut
+ receivedACtPrf, err := actPrfMigrator.dmOut.DataManager().GetActionProfile(actPrf.Tenant, actPrf.ID,
+ false, false, utils.NonTransactional)
+ if err != nil {
+ t.Error(err)
+ }
+ if !reflect.DeepEqual(receivedACtPrf, actPrf) {
+ t.Errorf("Expected %+v, received %+v", utils.ToJSON(actPrf), utils.ToJSON(receivedACtPrf))
+ }
+
+ //compared with dmIn(should be empty)
+ _, err = actPrfMigrator.dmIN.DataManager().GetActionProfile(actPrf.Tenant, actPrf.ID,
+ false, false, utils.NonTransactional)
+ if err != utils.ErrNotFound {
+ t.Error(err)
+ }
+ if actPrfMigrator.stats[utils.ActionProfiles] != 1 {
+ t.Errorf("Expected 1, received: %v", actPrfMigrator.stats[utils.ActionProfiles])
+ }
+ }
+}
diff --git a/migrator/migrator.go b/migrator/migrator.go
index c93e348c8..148be694c 100755
--- a/migrator/migrator.go
+++ b/migrator/migrator.go
@@ -134,6 +134,8 @@ func (m *Migrator) Migrate(taskIDs []string) (err error, stats map[string]int) {
err = m.migrateRatingPlans()
case utils.MetaRatingProfiles:
err = m.migrateRatingProfiles()
+ case utils.MetaActionProfiles:
+ err = m.migrateActionProfiles()
case utils.MetaDestinations:
err = m.migrateDestinations()
case utils.MetaReverseDestinations:
diff --git a/migrator/rateprofiles_it_test.go b/migrator/rateprofiles_it_test.go
index 202cd73d7..2c3d4ec9e 100644
--- a/migrator/rateprofiles_it_test.go
+++ b/migrator/rateprofiles_it_test.go
@@ -221,7 +221,7 @@ func testRatePrfITMigrateAndMove(t *testing.T) {
t.Fatal(err)
}
switch ratePrfAction {
- case utils.Migrate: // for the moment only one version of rate profiles exists
+ case utils.Migrate: //QQ for the moment only one version of rate profiles exists
case utils.Move:
if err := ratePrfMigrator.dmIN.DataManager().SetRateProfile(rPrf, true); err != nil {
t.Error(err)
diff --git a/utils/consts.go b/utils/consts.go
index c57e75942..4dd2d9f54 100755
--- a/utils/consts.go
+++ b/utils/consts.go
@@ -1101,6 +1101,7 @@ const (
TpDispatcherProfiles = "TpDispatcherProfiles"
TpDispatcherHosts = "TpDispatcherHosts"
TpRateProfiles = "TpRateProfiles"
+ TpActionProfiles = "TpActionProfiles"
)
// Dispatcher Const