diff --git a/admins/accounts.go b/admins/accounts.go
new file mode 100644
index 000000000..1bce51104
--- /dev/null
+++ b/admins/accounts.go
@@ -0,0 +1,163 @@
+/*
+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 admins
+
+import (
+ "time"
+
+ "github.com/cgrates/birpc/context"
+ "github.com/cgrates/cgrates/utils"
+)
+
+// GetAccount returns an Account
+func (admS *AdminS) V1GetAccount(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *utils.Account) 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 = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ ap, err := admS.dm.GetAccount(ctx, tnt, arg.ID)
+ if err != nil {
+ if err.Error() != utils.ErrNotFound.Error() {
+ err = utils.NewErrServerError(err)
+ }
+ return err
+ }
+ *reply = *ap
+ return nil
+}
+
+// GetAccountIDs returns list of account profile IDs registered for a tenant
+func (admS *AdminS) V1GetAccountIDs(ctx *context.Context, args *utils.ArgsItemIDs, actPrfIDs *[]string) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ prfx := utils.AccountPrefix + tnt + utils.ConcatenatedKeySep
+ lenPrfx := len(prfx)
+ prfx += args.ItemsPrefix
+ var keys []string
+ if keys, err = admS.dm.DataDB().GetKeysForPrefix(ctx, prfx); err != nil {
+ return
+ }
+ if len(keys) == 0 {
+ return utils.ErrNotFound
+ }
+ retIDs := make([]string, len(keys))
+ for i, key := range keys {
+ retIDs[i] = key[lenPrfx:]
+ }
+ var limit, offset, maxItems int
+ if limit, offset, maxItems, err = utils.GetPaginateOpts(args.APIOpts); err != nil {
+ return
+ }
+ *actPrfIDs, err = utils.Paginate(retIDs, limit, offset, maxItems)
+ return
+}
+
+// GetAccounts returns a list of accounts registered for a tenant
+func (admS *AdminS) V1GetAccounts(ctx *context.Context, args *utils.ArgsItemIDs, accs *[]*utils.Account) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ var accIDs []string
+ if err = admS.V1GetAccountIDs(ctx, args, &accIDs); err != nil {
+ return
+ }
+ *accs = make([]*utils.Account, 0, len(accIDs))
+ for _, accID := range accIDs {
+ var acc *utils.Account
+ acc, err = admS.dm.GetAccount(ctx, tnt, accID)
+ if err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *accs = append(*accs, acc)
+ }
+ return
+}
+
+// GetAccountsCount sets in reply var the total number of AccountIDs registered for a tenant
+// returns ErrNotFound in case of 0 AccountIDs
+func (admS *AdminS) V1GetAccountsCount(ctx *context.Context, args *utils.ArgsItemIDs, reply *int) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ prfx := utils.AccountPrefix + tnt + utils.ConcatenatedKeySep + args.ItemsPrefix
+ var keys []string
+ if keys, err = admS.dm.DataDB().GetKeysForPrefix(ctx, prfx); err != nil {
+ return err
+ }
+ if len(keys) == 0 {
+ return utils.ErrNotFound
+ }
+ *reply = len(keys)
+ return
+}
+
+// SetAccount add/update a new Account
+func (admS *AdminS) V1SetAccount(ctx *context.Context, args *utils.AccountWithAPIOpts, reply *string) error {
+ if missing := utils.MissingStructFields(args.Account, []string{utils.ID}); len(missing) != 0 {
+ return utils.NewErrMandatoryIeMissing(missing...)
+ }
+ if args.Tenant == utils.EmptyString {
+ args.Tenant = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ if err := admS.dm.SetAccount(ctx, args.Account, true); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //generate a loadID for CacheAccountProfiles and store it in database
+ if err := admS.dm.SetLoadIDs(ctx, map[string]int64{utils.CacheAccounts: time.Now().UnixNano()}); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ if err := admS.CallCache(ctx, utils.IfaceAsString(args.APIOpts[utils.MetaCache]), args.Tenant, utils.CacheAccounts,
+ args.TenantID(), utils.EmptyString, &args.FilterIDs, args.APIOpts); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = utils.OK
+ return nil
+}
+
+// RemoveAccount remove a specific Account
+func (admS *AdminS) V1RemoveAccount(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, 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 = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ if err := admS.dm.RemoveAccount(ctx, tnt, arg.ID,
+ true); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //generate a loadID for CacheAccountProfiles and store it in database
+ if err := admS.dm.SetLoadIDs(ctx, map[string]int64{utils.CacheAccounts: time.Now().UnixNano()}); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ if err := admS.CallCache(ctx, utils.IfaceAsString(arg.APIOpts[utils.MetaCache]), tnt, utils.CacheAccounts,
+ utils.ConcatenatedKey(tnt, arg.ID), utils.EmptyString, nil, arg.APIOpts); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = utils.OK
+ return nil
+}
diff --git a/admins/actions.go b/admins/actions.go
new file mode 100644
index 000000000..2202debdb
--- /dev/null
+++ b/admins/actions.go
@@ -0,0 +1,165 @@
+/*
+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 admins
+
+import (
+ "time"
+
+ "github.com/cgrates/birpc/context"
+ "github.com/cgrates/cgrates/engine"
+ "github.com/cgrates/cgrates/utils"
+)
+
+// GetActionProfile returns an Action Profile
+func (admS *AdminS) V1GetActionProfile(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, 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 = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ ap, err := admS.dm.GetActionProfile(ctx, 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 (admS *AdminS) V1GetActionProfileIDs(ctx *context.Context, args *utils.ArgsItemIDs, actPrfIDs *[]string) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ prfx := utils.ActionProfilePrefix + tnt + utils.ConcatenatedKeySep
+ lenPrfx := len(prfx)
+ prfx += args.ItemsPrefix
+ var keys []string
+ if keys, err = admS.dm.DataDB().GetKeysForPrefix(ctx, prfx); err != nil {
+ return
+ }
+ if len(keys) == 0 {
+ return utils.ErrNotFound
+ }
+ retIDs := make([]string, len(keys))
+ for i, key := range keys {
+ retIDs[i] = key[lenPrfx:]
+ }
+ var limit, offset, maxItems int
+ if limit, offset, maxItems, err = utils.GetPaginateOpts(args.APIOpts); err != nil {
+ return
+ }
+ *actPrfIDs, err = utils.Paginate(retIDs, limit, offset, maxItems)
+ return
+}
+
+// GetActionProfiles returns a list of action profiles registered for a tenant
+func (admS *AdminS) V1GetActionProfiles(ctx *context.Context, args *utils.ArgsItemIDs, actPrfs *[]*engine.ActionProfile) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ var actPrfIDs []string
+ if err = admS.V1GetActionProfileIDs(ctx, args, &actPrfIDs); err != nil {
+ return
+ }
+ *actPrfs = make([]*engine.ActionProfile, 0, len(actPrfIDs))
+ for _, actPrfID := range actPrfIDs {
+ var ap *engine.ActionProfile
+ ap, err = admS.dm.GetActionProfile(ctx, tnt, actPrfID, true, true, utils.NonTransactional)
+ if err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *actPrfs = append(*actPrfs, ap)
+ }
+ return
+}
+
+// GetActionProfilesCount sets in reply var the total number of ActionProfileIDs registered for a tenant
+// returns ErrNotFound in case of 0 ActionProfileIDs
+func (admS *AdminS) V1GetActionProfilesCount(ctx *context.Context, args *utils.ArgsItemIDs, reply *int) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ prfx := utils.ActionProfilePrefix + tnt + utils.ConcatenatedKeySep + args.ItemsPrefix
+ var keys []string
+ if keys, err = admS.dm.DataDB().GetKeysForPrefix(ctx, prfx); err != nil {
+ return err
+ }
+ if len(keys) == 0 {
+ return utils.ErrNotFound
+ }
+ *reply = len(keys)
+ return
+}
+
+// SetActionProfile add/update a new Action Profile
+func (admS *AdminS) V1SetActionProfile(ctx *context.Context, ap *engine.ActionProfileWithAPIOpts, 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 = admS.cfg.GeneralCfg().DefaultTenant
+ }
+
+ if err := admS.dm.SetActionProfile(ctx, ap.ActionProfile, true); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //generate a loadID for CacheActionProfiles and store it in database
+ if err := admS.dm.SetLoadIDs(ctx, map[string]int64{utils.CacheActionProfiles: time.Now().UnixNano()}); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ if err := admS.CallCache(ctx, utils.IfaceAsString(ap.APIOpts[utils.MetaCache]), ap.Tenant, utils.CacheActionProfiles,
+ ap.TenantID(), utils.EmptyString, &ap.FilterIDs, ap.APIOpts); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = utils.OK
+ return nil
+}
+
+// RemoveActionProfile remove a specific Action Profile
+func (admS *AdminS) V1RemoveActionProfile(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, 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 = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ if err := admS.dm.RemoveActionProfile(ctx, tnt, arg.ID,
+ true); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //generate a loadID for CacheActionProfiles and store it in database
+ if err := admS.dm.SetLoadIDs(ctx, map[string]int64{utils.CacheActionProfiles: time.Now().UnixNano()}); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ if err := admS.CallCache(ctx, utils.IfaceAsString(arg.APIOpts[utils.MetaCache]), tnt, utils.CacheActionProfiles,
+ utils.ConcatenatedKey(tnt, arg.ID), utils.EmptyString, nil, arg.APIOpts); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = utils.OK
+ return nil
+}
diff --git a/admins/admins.go b/admins/admins.go
new file mode 100644
index 000000000..1fe1020c4
--- /dev/null
+++ b/admins/admins.go
@@ -0,0 +1,50 @@
+/*
+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 admins
+
+import (
+ "github.com/cgrates/cgrates/config"
+ "github.com/cgrates/cgrates/engine"
+)
+
+func NewAdminS(cfg *config.CGRConfig, dm *engine.DataManager, connMgr *engine.ConnManager, fltrS *engine.FilterS,
+ storDBChan chan engine.StorDB) *AdminS {
+ storDB := <-storDBChan
+ return &AdminS{
+ cfg: cfg,
+ dm: dm,
+ storDB: storDB,
+ connMgr: connMgr,
+ fltrS: fltrS,
+
+ // TODO: Might be a good idea to pass the storDB channel to AdminSv1
+ // to be able to close the service the moment storDB is down (inside
+ // a ListenAndServe goroutine maybe)
+
+ // storDBChan: storDBChan,
+ }
+}
+
+type AdminS struct {
+ cfg *config.CGRConfig
+ dm *engine.DataManager
+ storDB engine.StorDB
+ connMgr *engine.ConnManager
+ fltrS *engine.FilterS
+}
diff --git a/admins/attributes.go b/admins/attributes.go
new file mode 100644
index 000000000..464850b1b
--- /dev/null
+++ b/admins/attributes.go
@@ -0,0 +1,168 @@
+/*
+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 admins
+
+import (
+ "time"
+
+ "github.com/cgrates/birpc/context"
+ "github.com/cgrates/cgrates/engine"
+ "github.com/cgrates/cgrates/utils"
+)
+
+// GetAttributeProfile returns an Attribute Profile based on the tenant and ID received
+func (admS *AdminS) V1GetAttributeProfile(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *engine.APIAttributeProfile) (err 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 = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ if attrPrf, err := admS.dm.GetAttributeProfile(ctx, tnt, arg.ID, true, true, utils.NonTransactional); err != nil {
+ return utils.APIErrorHandler(err)
+ } else {
+ attr := engine.NewAPIAttributeProfile(attrPrf)
+ *reply = *attr
+ }
+ return nil
+}
+
+// GetAttributeProfileIDs returns list of attributeProfile IDs registered for a tenant
+func (admS *AdminS) V1GetAttributeProfileIDs(ctx *context.Context, args *utils.ArgsItemIDs, attrPrfIDs *[]string) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ prfx := utils.AttributeProfilePrefix + tnt + utils.ConcatenatedKeySep
+ lenPrfx := len(prfx)
+ prfx += args.ItemsPrefix
+ var keys []string
+ if keys, err = admS.dm.DataDB().GetKeysForPrefix(ctx, prfx); err != nil {
+ return
+ }
+ if len(keys) == 0 {
+ return utils.ErrNotFound
+ }
+ retIDs := make([]string, len(keys))
+ for i, key := range keys {
+ retIDs[i] = key[lenPrfx:]
+ }
+ var limit, offset, maxItems int
+ if limit, offset, maxItems, err = utils.GetPaginateOpts(args.APIOpts); err != nil {
+ return
+ }
+ *attrPrfIDs, err = utils.Paginate(retIDs, limit, offset, maxItems)
+ return
+}
+
+// GetAttributeProfiles returns a list of attribute profiles registered for a tenant
+func (admS *AdminS) V1GetAttributeProfiles(ctx *context.Context, args *utils.ArgsItemIDs, attrPrfs *[]*engine.APIAttributeProfile) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ var attrPrfIDs []string
+ if err = admS.V1GetAttributeProfileIDs(ctx, args, &attrPrfIDs); err != nil {
+ return
+ }
+ *attrPrfs = make([]*engine.APIAttributeProfile, 0, len(attrPrfIDs))
+ for _, attrPrfID := range attrPrfIDs {
+ var ap *engine.AttributeProfile
+ ap, err = admS.dm.GetAttributeProfile(ctx, tnt, attrPrfID, true, true, utils.NonTransactional)
+ if err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ attr := engine.NewAPIAttributeProfile(ap)
+ *attrPrfs = append(*attrPrfs, attr)
+ }
+ return
+}
+
+// GetAttributeProfilesCount returns the total number of AttributeProfileIDs registered for a tenant
+// returns ErrNotFound in case of 0 AttributeProfileIDs
+func (admS *AdminS) V1GetAttributeProfilesCount(ctx *context.Context, args *utils.ArgsItemIDs, reply *int) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ prfx := utils.AttributeProfilePrefix + tnt + utils.ConcatenatedKeySep + args.ItemsPrefix
+ var keys []string
+ if keys, err = admS.dm.DataDB().GetKeysForPrefix(ctx, prfx); err != nil {
+ return err
+ }
+ if len(keys) == 0 {
+ return utils.ErrNotFound
+ }
+ *reply = len(keys)
+ return
+}
+
+// SetAttributeProfile add/update a new Attribute Profile
+func (admS *AdminS) V1SetAttributeProfile(ctx *context.Context, arg *engine.APIAttributeProfileWithAPIOpts, reply *string) error {
+ if missing := utils.MissingStructFields(arg.APIAttributeProfile, []string{utils.ID}); len(missing) != 0 {
+ return utils.NewErrMandatoryIeMissing(missing...)
+ }
+ if arg.Tenant == utils.EmptyString {
+ arg.Tenant = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ alsPrf, err := arg.APIAttributeProfile.AsAttributeProfile()
+ if err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ if err := admS.dm.SetAttributeProfile(ctx, alsPrf, true); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //generate a loadID for CacheAttributeProfiles and store it in database
+ if err := admS.dm.SetLoadIDs(ctx,
+ map[string]int64{utils.CacheAttributeProfiles: time.Now().UnixNano()}); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ if err := admS.CallCache(ctx, utils.IfaceAsString(arg.APIOpts[utils.MetaCache]), alsPrf.Tenant, utils.CacheAttributeProfiles,
+ alsPrf.TenantID(), utils.EmptyString, &alsPrf.FilterIDs, arg.APIOpts); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = utils.OK
+ return nil
+}
+
+// RemoveAttributeProfile remove a specific Attribute Profile based on tenant an ID
+func (admS *AdminS) V1RemoveAttributeProfile(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, 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 = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ if err := admS.dm.RemoveAttributeProfile(ctx, tnt, arg.ID,
+ true); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //generate a loadID for CacheAttributeProfiles and store it in database
+ if err := admS.dm.SetLoadIDs(ctx, map[string]int64{utils.CacheAttributeProfiles: time.Now().UnixNano()}); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ if err := admS.CallCache(ctx, utils.IfaceAsString(arg.APIOpts[utils.MetaCache]), tnt, utils.CacheAttributeProfiles,
+ utils.ConcatenatedKey(tnt, arg.ID), utils.EmptyString, nil, arg.APIOpts); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = utils.OK
+ return nil
+}
diff --git a/admins/cdrs.go b/admins/cdrs.go
new file mode 100644
index 000000000..8568702d2
--- /dev/null
+++ b/admins/cdrs.go
@@ -0,0 +1,61 @@
+/*
+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 admins
+
+import (
+ "fmt"
+
+ "github.com/cgrates/birpc/context"
+
+ "github.com/cgrates/cgrates/engine"
+ "github.com/cgrates/cgrates/utils"
+)
+
+// GetCDRs retrieves a list of CDRs matching the specified filters.
+func (admS AdminS) V1GetCDRs(ctx *context.Context, args *utils.CDRFilters, reply *[]*utils.CDR) error {
+ if args.Tenant == utils.EmptyString {
+ args.Tenant = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ fltrs, err := engine.GetFilters(ctx, args.FilterIDs, args.Tenant, admS.dm)
+ if err != nil {
+ return fmt.Errorf("preparing filters failed: %w", err)
+ }
+ cdrs, err := admS.storDB.GetCDRs(ctx, fltrs, args.APIOpts)
+ if err != nil {
+ return fmt.Errorf("retrieving CDRs failed: %w", err)
+ }
+ *reply = cdrs
+ return nil
+}
+
+// RemoveCDRs removes CDRs matching the specified filters.
+func (admS AdminS) V1RemoveCDRs(ctx *context.Context, args *utils.CDRFilters, reply *string) (err error) {
+ if args.Tenant == utils.EmptyString {
+ args.Tenant = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ fltrs, err := engine.GetFilters(ctx, args.FilterIDs, args.Tenant, admS.dm)
+ if err != nil {
+ return fmt.Errorf("preparing filters failed: %w", err)
+ }
+ if err := admS.storDB.RemoveCDRs(ctx, fltrs); err != nil {
+ return fmt.Errorf("removing CDRs failed: %w", err)
+ }
+ *reply = utils.OK
+ return
+}
diff --git a/admins/chargers.go b/admins/chargers.go
new file mode 100644
index 000000000..d0d21b2f4
--- /dev/null
+++ b/admins/chargers.go
@@ -0,0 +1,169 @@
+/*
+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 admins
+
+import (
+ "time"
+
+ "github.com/cgrates/birpc/context"
+
+ "github.com/cgrates/cgrates/engine"
+ "github.com/cgrates/cgrates/utils"
+)
+
+// GetChargerProfile returns a Charger Profile
+func (adms *AdminS) V1GetChargerProfile(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *engine.ChargerProfile) 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 = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ if cpp, err := adms.dm.GetChargerProfile(ctx, tnt, arg.ID, true, true, utils.NonTransactional); err != nil {
+ return utils.APIErrorHandler(err)
+ } else {
+ *reply = *cpp
+ }
+ return nil
+}
+
+// GetChargerProfileIDs returns list of chargerProfile IDs registered for a tenant
+func (adms *AdminS) V1GetChargerProfileIDs(ctx *context.Context, args *utils.ArgsItemIDs, chPrfIDs *[]string) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ prfx := utils.ChargerProfilePrefix + tnt + utils.ConcatenatedKeySep
+ lenPrfx := len(prfx)
+ prfx += args.ItemsPrefix
+ var keys []string
+ if keys, err = adms.dm.DataDB().GetKeysForPrefix(ctx, prfx); err != nil {
+ return
+ }
+ if len(keys) == 0 {
+ return utils.ErrNotFound
+ }
+ retIDs := make([]string, len(keys))
+ for i, key := range keys {
+ retIDs[i] = key[lenPrfx:]
+ }
+ var limit, offset, maxItems int
+ if limit, offset, maxItems, err = utils.GetPaginateOpts(args.APIOpts); err != nil {
+ return
+ }
+ *chPrfIDs, err = utils.Paginate(retIDs, limit, offset, maxItems)
+ return
+}
+
+// GetChargerProfiles returns a list of charger profiles registered for a tenant
+func (admS *AdminS) V1GetChargerProfiles(ctx *context.Context, args *utils.ArgsItemIDs, chrgPrfs *[]*engine.ChargerProfile) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ var chrgPrfIDs []string
+ if err = admS.V1GetChargerProfileIDs(ctx, args, &chrgPrfIDs); err != nil {
+ return
+ }
+ *chrgPrfs = make([]*engine.ChargerProfile, 0, len(chrgPrfIDs))
+ for _, chrgPrfID := range chrgPrfIDs {
+ var chgrPrf *engine.ChargerProfile
+ chgrPrf, err = admS.dm.GetChargerProfile(ctx, tnt, chrgPrfID, true, true, utils.NonTransactional)
+ if err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *chrgPrfs = append(*chrgPrfs, chgrPrf)
+ }
+ return
+}
+
+// GetChargerProfilesCount returns the total number of ChargerProfiles registered for a tenant
+// returns ErrNotFound in case of 0 ChargerProfiles
+func (admS *AdminS) V1GetChargerProfilesCount(ctx *context.Context, args *utils.ArgsItemIDs, reply *int) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ prfx := utils.ChargerProfilePrefix + tnt + utils.ConcatenatedKeySep + args.ItemsPrefix
+ var keys []string
+ if keys, err = admS.dm.DataDB().GetKeysForPrefix(ctx, prfx); err != nil {
+ return err
+ }
+ if len(keys) == 0 {
+ return utils.ErrNotFound
+ }
+ *reply = len(keys)
+ return
+}
+
+type ChargerWithAPIOpts struct {
+ *engine.ChargerProfile
+ APIOpts map[string]any
+}
+
+// SetChargerProfile add/update a new Charger Profile
+func (adms *AdminS) V1SetChargerProfile(ctx *context.Context, arg *ChargerWithAPIOpts, reply *string) error {
+ if missing := utils.MissingStructFields(arg.ChargerProfile, []string{utils.ID}); len(missing) != 0 {
+ return utils.NewErrMandatoryIeMissing(missing...)
+ }
+ if arg.Tenant == utils.EmptyString {
+ arg.Tenant = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ if err := adms.dm.SetChargerProfile(ctx, arg.ChargerProfile, true); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //generate a loadID for CacheChargerProfiles and store it in database
+ if err := adms.dm.SetLoadIDs(ctx, map[string]int64{utils.CacheChargerProfiles: time.Now().UnixNano()}); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //handle caching for ChargerProfile
+ if err := adms.CallCache(ctx, utils.IfaceAsString(arg.APIOpts[utils.MetaCache]), arg.Tenant, utils.CacheChargerProfiles,
+ arg.TenantID(), utils.EmptyString, &arg.FilterIDs, arg.APIOpts); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = utils.OK
+ return nil
+}
+
+// RemoveChargerProfile remove a specific Charger Profile
+func (adms *AdminS) V1RemoveChargerProfile(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, 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 = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ if err := adms.dm.RemoveChargerProfile(ctx, tnt,
+ arg.ID, true); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //generate a loadID for CacheChargerProfiles and store it in database
+ if err := adms.dm.SetLoadIDs(ctx, map[string]int64{utils.CacheChargerProfiles: time.Now().UnixNano()}); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //handle caching for ChargerProfile
+ if err := adms.CallCache(ctx, utils.IfaceAsString(arg.APIOpts[utils.MetaCache]), tnt, utils.CacheChargerProfiles,
+ utils.ConcatenatedKey(tnt, arg.ID), utils.EmptyString, nil, arg.APIOpts); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = utils.OK
+ return nil
+}
diff --git a/admins/dispatchers.go b/admins/dispatchers.go
new file mode 100644
index 000000000..d7cd500b1
--- /dev/null
+++ b/admins/dispatchers.go
@@ -0,0 +1,316 @@
+/*
+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 admins
+
+import (
+ "time"
+
+ "github.com/cgrates/birpc/context"
+ "github.com/cgrates/cgrates/engine"
+ "github.com/cgrates/cgrates/utils"
+)
+
+// GetDispatcherProfile returns a Dispatcher Profile
+func (admS *AdminS) V1GetDispatcherProfile(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *engine.DispatcherProfile) 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 = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ dpp, err := admS.dm.GetDispatcherProfile(ctx, tnt, arg.ID, true, true, utils.NonTransactional)
+ if err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = *dpp
+ return nil
+}
+
+// GetDispatcherProfileIDs returns list of dispatcherProfile IDs registered for a tenant
+func (admS *AdminS) V1GetDispatcherProfileIDs(ctx *context.Context, args *utils.ArgsItemIDs, dPrfIDs *[]string) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ prfx := utils.DispatcherProfilePrefix + tnt + utils.ConcatenatedKeySep
+ lenPrfx := len(prfx)
+ prfx += args.ItemsPrefix
+ var keys []string
+ if keys, err = admS.dm.DataDB().GetKeysForPrefix(ctx, prfx); err != nil {
+ return
+ }
+ if len(keys) == 0 {
+ return utils.ErrNotFound
+ }
+ retIDs := make([]string, len(keys))
+ for i, key := range keys {
+ retIDs[i] = key[lenPrfx:]
+ }
+ var limit, offset, maxItems int
+ if limit, offset, maxItems, err = utils.GetPaginateOpts(args.APIOpts); err != nil {
+ return
+ }
+ *dPrfIDs, err = utils.Paginate(retIDs, limit, offset, maxItems)
+ return
+}
+
+// GetDispatcherProfiles returns a list of dispatcher profiles registered for a tenant
+func (admS *AdminS) V1GetDispatcherProfiles(ctx *context.Context, args *utils.ArgsItemIDs, dspPrfs *[]*engine.DispatcherProfile) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ var dspPrfIDs []string
+ if err = admS.V1GetDispatcherProfileIDs(ctx, args, &dspPrfIDs); err != nil {
+ return
+ }
+ *dspPrfs = make([]*engine.DispatcherProfile, 0, len(dspPrfIDs))
+ for _, dspPrfID := range dspPrfIDs {
+ var dspPrf *engine.DispatcherProfile
+ dspPrf, err = admS.dm.GetDispatcherProfile(ctx, tnt, dspPrfID, true, true, utils.NonTransactional)
+ if err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *dspPrfs = append(*dspPrfs, dspPrf)
+ }
+ return
+}
+
+// GetDispatcherProfilesCount returns the total number of DispatcherProfiles registered for a tenant
+// returns ErrNotFound in case of 0 DispatcherProfiles
+func (admS *AdminS) V1GetDispatcherProfilesCount(ctx *context.Context, args *utils.ArgsItemIDs, reply *int) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ prfx := utils.DispatcherProfilePrefix + tnt + utils.ConcatenatedKeySep + args.ItemsPrefix
+ var keys []string
+ if keys, err = admS.dm.DataDB().GetKeysForPrefix(ctx, prfx); err != nil {
+ return err
+ }
+ if len(keys) == 0 {
+ return utils.ErrNotFound
+ }
+ *reply = len(keys)
+ return
+}
+
+type DispatcherWithAPIOpts struct {
+ *engine.DispatcherProfile
+ APIOpts map[string]any
+}
+
+// SetDispatcherProfile add/update a new Dispatcher Profile
+func (admS *AdminS) V1SetDispatcherProfile(ctx *context.Context, args *DispatcherWithAPIOpts, reply *string) error {
+ if missing := utils.MissingStructFields(args.DispatcherProfile, []string{utils.ID}); len(missing) != 0 {
+ return utils.NewErrMandatoryIeMissing(missing...)
+ }
+ if args.Tenant == utils.EmptyString {
+ args.Tenant = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ if err := admS.dm.SetDispatcherProfile(ctx, args.DispatcherProfile, true); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //generate a loadID for CacheDispatcherProfiles and store it in database
+ if err := admS.dm.SetLoadIDs(ctx, map[string]int64{utils.CacheDispatcherProfiles: time.Now().UnixNano()}); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //handle caching for DispatcherProfile
+ if err := admS.CallCache(ctx, utils.IfaceAsString(args.APIOpts[utils.MetaCache]), args.Tenant, utils.CacheDispatcherProfiles,
+ args.TenantID(), utils.EmptyString, &args.FilterIDs, args.APIOpts); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //handle caching for DispatchersInstance
+ cacheAct := utils.MetaRemove
+ if err := admS.CallCache(ctx, utils.FirstNonEmpty(utils.IfaceAsString(args.APIOpts[utils.MetaCache]), cacheAct),
+ args.Tenant, utils.CacheDispatchers, args.TenantID(), utils.EmptyString, &args.FilterIDs, args.APIOpts); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //handle caching for DispatcherRoutes
+ if err := admS.CallCache(ctx, utils.FirstNonEmpty(utils.IfaceAsString(args.APIOpts[utils.MetaCache]), cacheAct),
+ args.Tenant, utils.CacheDispatcherRoutes, args.TenantID(),
+ utils.ConcatenatedKey(utils.CacheDispatcherProfiles, args.Tenant, args.ID),
+ &args.FilterIDs, args.APIOpts); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = utils.OK
+ return nil
+}
+
+// RemoveDispatcherProfile remove a specific Dispatcher Profile
+func (admS *AdminS) V1RemoveDispatcherProfile(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, 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 = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ if err := admS.dm.RemoveDispatcherProfile(ctx, tnt,
+ arg.ID, true); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //generate a loadID for CacheDispatcherProfiles and store it in database
+ if err := admS.dm.SetLoadIDs(ctx, map[string]int64{utils.CacheDispatcherProfiles: time.Now().UnixNano()}); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //handle caching for DispatcherProfile
+ if err := admS.CallCache(ctx, utils.IfaceAsString(arg.APIOpts[utils.MetaCache]), tnt, utils.CacheDispatcherProfiles,
+ utils.ConcatenatedKey(tnt, arg.ID), utils.EmptyString, nil, arg.APIOpts); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = utils.OK
+ return nil
+}
+
+// GetDispatcherHost returns a Dispatcher Host
+func (admS *AdminS) V1GetDispatcherHost(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *engine.DispatcherHost) 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 = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ dpp, err := admS.dm.GetDispatcherHost(ctx, tnt, arg.ID, true, false, utils.NonTransactional)
+ if err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = *dpp
+ return nil
+}
+
+// GetDispatcherHostIDs returns list of dispatcherHost IDs registered for a tenant
+func (admS *AdminS) V1GetDispatcherHostIDs(ctx *context.Context, args *utils.ArgsItemIDs, dspHostIDs *[]string) (err error) {
+ tenant := args.Tenant
+ if tenant == utils.EmptyString {
+ tenant = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ prfx := utils.DispatcherHostPrefix + tenant + utils.ConcatenatedKeySep
+ lenPrfx := len(prfx)
+ prfx += args.ItemsPrefix
+ var keys []string
+ if keys, err = admS.dm.DataDB().GetKeysForPrefix(ctx, prfx); err != nil {
+ return err
+ }
+ if len(keys) == 0 {
+ return utils.ErrNotFound
+ }
+ retIDs := make([]string, len(keys))
+ for i, key := range keys {
+ retIDs[i] = key[lenPrfx:]
+ }
+ var limit, offset, maxItems int
+ if limit, offset, maxItems, err = utils.GetPaginateOpts(args.APIOpts); err != nil {
+ return
+ }
+ *dspHostIDs, err = utils.Paginate(retIDs, limit, offset, maxItems)
+ return
+}
+
+// GetDispatcherHosts returns a list of dispatcher hosts registered for a tenant
+func (admS *AdminS) V1GetDispatcherHosts(ctx *context.Context, args *utils.ArgsItemIDs, dspHosts *[]*engine.DispatcherHost) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ var dspHostIDs []string
+ if err = admS.V1GetDispatcherHostIDs(ctx, args, &dspHostIDs); err != nil {
+ return
+ }
+ *dspHosts = make([]*engine.DispatcherHost, 0, len(dspHostIDs))
+ for _, dspHostID := range dspHostIDs {
+ var dspHost *engine.DispatcherHost
+ dspHost, err = admS.dm.GetDispatcherHost(ctx, tnt, dspHostID, true, true, utils.NonTransactional)
+ if err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *dspHosts = append(*dspHosts, dspHost)
+ }
+ return
+}
+
+// GetDispatcherHostsCount returns the total number of DispatcherHosts registered for a tenant
+// returns ErrNotFound in case of 0 DispatcherHosts
+func (admS *AdminS) V1GetDispatcherHostsCount(ctx *context.Context, args *utils.ArgsItemIDs, reply *int) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ prfx := utils.DispatcherHostPrefix + tnt + utils.ConcatenatedKeySep + args.ItemsPrefix
+ var keys []string
+ if keys, err = admS.dm.DataDB().GetKeysForPrefix(ctx, prfx); err != nil {
+ return err
+ }
+ if len(keys) == 0 {
+ return utils.ErrNotFound
+ }
+ *reply = len(keys)
+ return
+}
+
+// SetDispatcherHost add/update a new Dispatcher Host
+func (admS *AdminS) V1SetDispatcherHost(ctx *context.Context, args *engine.DispatcherHostWithAPIOpts, reply *string) error {
+ if missing := utils.MissingStructFields(args.DispatcherHost, []string{utils.ID}); len(missing) != 0 {
+ return utils.NewErrMandatoryIeMissing(missing...)
+ }
+ if args.Tenant == utils.EmptyString {
+ args.Tenant = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ if err := admS.dm.SetDispatcherHost(ctx, args.DispatcherHost); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //generate a loadID for CacheDispatcherHosts and store it in database
+ if err := admS.dm.SetLoadIDs(ctx, map[string]int64{utils.CacheDispatcherHosts: time.Now().UnixNano()}); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //handle caching for DispatcherProfile
+ if err := admS.CallCache(ctx, utils.IfaceAsString(args.APIOpts[utils.MetaCache]), args.Tenant, utils.CacheDispatcherHosts,
+ args.TenantID(), utils.EmptyString, nil, args.APIOpts); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = utils.OK
+ return nil
+}
+
+// RemoveDispatcherHost remove a specific Dispatcher Host
+func (admS *AdminS) V1RemoveDispatcherHost(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, 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 = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ if err := admS.dm.RemoveDispatcherHost(ctx, tnt, arg.ID); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //generate a loadID for CacheDispatcherHosts and store it in database
+ if err := admS.dm.SetLoadIDs(ctx, map[string]int64{utils.CacheDispatcherHosts: time.Now().UnixNano()}); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //handle caching for DispatcherProfile
+ if err := admS.CallCache(ctx, utils.IfaceAsString(arg.APIOpts[utils.MetaCache]), tnt, utils.CacheDispatcherHosts,
+ utils.ConcatenatedKey(tnt, arg.ID), utils.EmptyString, nil, arg.APIOpts); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = utils.OK
+ return nil
+}
diff --git a/admins/filter_indexes.go b/admins/filter_indexes.go
new file mode 100644
index 000000000..c70cd0bf8
--- /dev/null
+++ b/admins/filter_indexes.go
@@ -0,0 +1,842 @@
+/*
+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 admins
+
+import (
+ "slices"
+ "strings"
+ "time"
+
+ "github.com/cgrates/birpc/context"
+ "github.com/cgrates/cgrates/engine"
+ "github.com/cgrates/cgrates/utils"
+ "github.com/cgrates/ltcache"
+)
+
+type AttrGetFilterIndexes struct {
+ Tenant string
+ Context string
+ ItemType string
+ FilterType string
+ FilterField string
+ FilterValue string
+ APIOpts map[string]any
+}
+
+type AttrRemFilterIndexes struct {
+ Tenant string
+ Context string
+ ItemType string
+ APIOpts map[string]any
+}
+
+func (adms *AdminS) V1RemoveFilterIndexes(ctx *context.Context, arg *AttrRemFilterIndexes, reply *string) (err error) {
+ if missing := utils.MissingStructFields(arg, []string{"ItemType"}); len(missing) != 0 { //Params missing
+ return utils.NewErrMandatoryIeMissing(missing...)
+ }
+ tnt := arg.Tenant
+ if tnt == utils.EmptyString {
+ tnt = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ tntCtx := tnt
+ switch arg.ItemType {
+ case utils.MetaThresholds:
+ arg.ItemType = utils.CacheThresholdFilterIndexes
+ case utils.MetaRoutes:
+ arg.ItemType = utils.CacheRouteFilterIndexes
+ case utils.MetaStats:
+ arg.ItemType = utils.CacheStatFilterIndexes
+ case utils.MetaResources:
+ arg.ItemType = utils.CacheResourceFilterIndexes
+ case utils.MetaChargers:
+ arg.ItemType = utils.CacheChargerFilterIndexes
+ case utils.MetaAccounts:
+ arg.ItemType = utils.CacheAccountsFilterIndexes
+ case utils.MetaActions:
+ arg.ItemType = utils.CacheActionProfilesFilterIndexes
+ case utils.MetaRateProfiles:
+ arg.ItemType = utils.CacheRateProfilesFilterIndexes
+ case utils.MetaRateProfileRates:
+ if missing := utils.MissingStructFields(arg, []string{"Context"}); len(missing) != 0 {
+ return utils.NewErrMandatoryIeMissing(missing...)
+ }
+ arg.ItemType = utils.CacheRateFilterIndexes
+ tntCtx = utils.ConcatenatedKey(tnt, arg.Context)
+ case utils.MetaDispatchers:
+ arg.ItemType = utils.CacheDispatcherFilterIndexes
+ case utils.MetaAttributes:
+ arg.ItemType = utils.CacheAttributeFilterIndexes
+ }
+ if err = adms.dm.RemoveIndexes(ctx, arg.ItemType, tntCtx, utils.EmptyString); err != nil {
+ return
+ }
+ //generate a loadID for CacheFilterIndexes and store it in database
+ if err := adms.dm.SetLoadIDs(ctx,
+ map[string]int64{arg.ItemType: time.Now().UnixNano()}); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ if err := adms.callCacheForRemoveIndexes(ctx, utils.IfaceAsString(arg.APIOpts[utils.MetaCache]), arg.Tenant,
+ arg.ItemType, []string{utils.MetaAny}, arg.APIOpts); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = utils.OK
+ return
+}
+
+func (adms *AdminS) V1GetFilterIndexes(ctx *context.Context, arg *AttrGetFilterIndexes, reply *[]string) (err error) {
+ var indexes map[string]utils.StringSet
+ var indexedSlice []string
+ indexesFilter := make(map[string]utils.StringSet)
+ if missing := utils.MissingStructFields(arg, []string{"ItemType"}); len(missing) != 0 { //Params missing
+ return utils.NewErrMandatoryIeMissing(missing...)
+ }
+ tnt := arg.Tenant
+ if tnt == utils.EmptyString {
+ tnt = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ tntCtx := tnt
+ switch arg.ItemType {
+ case utils.MetaThresholds:
+ arg.ItemType = utils.CacheThresholdFilterIndexes
+ case utils.MetaRoutes:
+ arg.ItemType = utils.CacheRouteFilterIndexes
+ case utils.MetaStats:
+ arg.ItemType = utils.CacheStatFilterIndexes
+ case utils.MetaResources:
+ arg.ItemType = utils.CacheResourceFilterIndexes
+ case utils.MetaChargers:
+ arg.ItemType = utils.CacheChargerFilterIndexes
+ case utils.MetaAccounts:
+ arg.ItemType = utils.CacheAccountsFilterIndexes
+ case utils.MetaActions:
+ arg.ItemType = utils.CacheActionProfilesFilterIndexes
+ case utils.MetaRateProfiles:
+ arg.ItemType = utils.CacheRateProfilesFilterIndexes
+ case utils.MetaRateProfileRates:
+ if missing := utils.MissingStructFields(arg, []string{"Context"}); len(missing) != 0 {
+ return utils.NewErrMandatoryIeMissing(missing...)
+ }
+ arg.ItemType = utils.CacheRateFilterIndexes
+ tntCtx = utils.ConcatenatedKey(tnt, arg.Context)
+ case utils.MetaDispatchers:
+ arg.ItemType = utils.CacheDispatcherFilterIndexes
+ case utils.MetaAttributes:
+ arg.ItemType = utils.CacheAttributeFilterIndexes
+ }
+ if indexes, err = adms.dm.GetIndexes(ctx,
+ arg.ItemType, tntCtx, utils.EmptyString, utils.EmptyString, true, true); err != nil {
+ return
+ }
+ if arg.FilterType != utils.EmptyString {
+ for val, strmap := range indexes {
+ if strings.HasPrefix(val, arg.FilterType) {
+ indexesFilter[val] = strmap
+ for _, value := range strmap.AsSlice() {
+ indexedSlice = append(indexedSlice, utils.ConcatenatedKey(val, value))
+ }
+ }
+ }
+ if len(indexedSlice) == 0 {
+ return utils.ErrNotFound
+ }
+ }
+ if arg.FilterField != utils.EmptyString {
+ if len(indexedSlice) == 0 {
+ indexesFilter = make(map[string]utils.StringSet)
+ for val, strmap := range indexes {
+ if strings.Contains(val, arg.FilterField) {
+ indexesFilter[val] = strmap
+ for _, value := range strmap.AsSlice() {
+ indexedSlice = append(indexedSlice, utils.ConcatenatedKey(val, value))
+ }
+ }
+ }
+ if len(indexedSlice) == 0 {
+ return utils.ErrNotFound
+ }
+ } else {
+ var cloneIndexSlice []string
+ for val, strmap := range indexesFilter {
+ if strings.Contains(val, arg.FilterField) {
+ for _, value := range strmap.AsSlice() {
+ cloneIndexSlice = append(cloneIndexSlice, utils.ConcatenatedKey(val, value))
+ }
+ }
+ }
+ if len(cloneIndexSlice) == 0 {
+ return utils.ErrNotFound
+ }
+ indexedSlice = cloneIndexSlice
+ }
+ }
+ if arg.FilterValue != utils.EmptyString {
+ if len(indexedSlice) == 0 {
+ for val, strmap := range indexes {
+ if strings.Contains(val, arg.FilterValue) {
+ for _, value := range strmap.AsSlice() {
+ indexedSlice = append(indexedSlice, utils.ConcatenatedKey(val, value))
+ }
+ }
+ }
+ if len(indexedSlice) == 0 {
+ return utils.ErrNotFound
+ }
+ } else {
+ var cloneIndexSlice []string
+ for val, strmap := range indexesFilter {
+ if strings.Contains(val, arg.FilterValue) {
+ for _, value := range strmap.AsSlice() {
+ cloneIndexSlice = append(cloneIndexSlice, utils.ConcatenatedKey(val, value))
+ }
+ }
+ }
+ if len(cloneIndexSlice) == 0 {
+ return utils.ErrNotFound
+ }
+ indexedSlice = cloneIndexSlice
+ }
+ }
+ if len(indexedSlice) == 0 {
+ for val, strmap := range indexes {
+ for _, value := range strmap.AsSlice() {
+ indexedSlice = append(indexedSlice, utils.ConcatenatedKey(val, value))
+ }
+ }
+ }
+ var limit, offset, maxItems int
+ if limit, offset, maxItems, err = utils.GetPaginateOpts(arg.APIOpts); err != nil {
+ return
+ }
+ *reply, err = utils.Paginate(indexedSlice, limit, offset, maxItems)
+ return
+}
+
+// ComputeFilterIndexes selects which index filters to recompute
+func (adms *AdminS) V1ComputeFilterIndexes(ctx *context.Context, args *utils.ArgsComputeFilterIndexes, reply *string) (err error) {
+ transactionID := utils.GenUUID()
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ cacheIDs := make(map[string][]string)
+
+ var indexes utils.StringSet
+ //ThresholdProfile Indexes
+ if args.ThresholdS {
+ cacheIDs[utils.CacheThresholdFilterIndexes] = []string{utils.MetaAny}
+ if indexes, err = engine.ComputeIndexes(ctx, adms.dm, tnt, utils.EmptyString, utils.CacheThresholdFilterIndexes,
+ nil, transactionID, func(tnt, id, grp string) (*[]string, error) {
+ th, e := adms.dm.GetThresholdProfile(ctx, tnt, id, true, false, utils.NonTransactional)
+ if e != nil {
+ return nil, e
+ }
+ return utils.SliceStringPointer(slices.Clone(th.FilterIDs)), nil
+ }, nil); err != nil && err != utils.ErrNotFound {
+ return utils.APIErrorHandler(err)
+ }
+ args.ThresholdS = indexes.Size() != 0
+ }
+ //StatQueueProfile Indexes
+ if args.StatS {
+ cacheIDs[utils.CacheStatFilterIndexes] = []string{utils.MetaAny}
+ if indexes, err = engine.ComputeIndexes(ctx, adms.dm, tnt, utils.EmptyString, utils.CacheStatFilterIndexes,
+ nil, transactionID, func(tnt, id, grp string) (*[]string, error) {
+ sq, e := adms.dm.GetStatQueueProfile(ctx, tnt, id, true, false, utils.NonTransactional)
+ if e != nil {
+ return nil, e
+ }
+ return utils.SliceStringPointer(slices.Clone(sq.FilterIDs)), nil
+ }, nil); err != nil && err != utils.ErrNotFound {
+ return utils.APIErrorHandler(err)
+ }
+ args.StatS = indexes.Size() != 0
+ }
+ //ResourceProfile Indexes
+ if args.ResourceS {
+ cacheIDs[utils.CacheResourceFilterIndexes] = []string{utils.MetaAny}
+ if indexes, err = engine.ComputeIndexes(ctx, adms.dm, tnt, utils.EmptyString, utils.CacheResourceFilterIndexes,
+ nil, transactionID, func(tnt, id, grp string) (*[]string, error) {
+ rp, e := adms.dm.GetResourceProfile(ctx, tnt, id, true, false, utils.NonTransactional)
+ if e != nil {
+ return nil, e
+ }
+ return utils.SliceStringPointer(slices.Clone(rp.FilterIDs)), nil
+ }, nil); err != nil && err != utils.ErrNotFound {
+ return utils.APIErrorHandler(err)
+ }
+ args.ResourceS = indexes.Size() != 0
+ }
+ //RouteSProfile Indexes
+ if args.RouteS {
+ cacheIDs[utils.CacheRouteFilterIndexes] = []string{utils.MetaAny}
+ if indexes, err = engine.ComputeIndexes(ctx, adms.dm, tnt, utils.EmptyString, utils.CacheRouteFilterIndexes,
+ nil, transactionID, func(tnt, id, grp string) (*[]string, error) {
+ rp, e := adms.dm.GetRouteProfile(ctx, tnt, id, true, false, utils.NonTransactional)
+ if e != nil {
+ return nil, e
+ }
+ return utils.SliceStringPointer(slices.Clone(rp.FilterIDs)), nil
+ }, nil); err != nil && err != utils.ErrNotFound {
+ return utils.APIErrorHandler(err)
+ }
+ args.RouteS = indexes.Size() != 0
+ }
+ //AttributeProfile Indexes
+ if args.AttributeS {
+ cacheIDs[utils.CacheAttributeFilterIndexes] = []string{utils.MetaAny}
+ if indexes, err = engine.ComputeIndexes(ctx, adms.dm, tnt, utils.EmptyString, utils.CacheAttributeFilterIndexes,
+ nil, transactionID, func(tnt, id, grp string) (*[]string, error) {
+ attr, e := adms.dm.GetAttributeProfile(ctx, tnt, id, true, false, utils.NonTransactional)
+ if e != nil {
+ return nil, e
+ }
+ return utils.SliceStringPointer(slices.Clone(attr.FilterIDs)), nil
+ }, nil); err != nil && err != utils.ErrNotFound {
+ return utils.APIErrorHandler(err)
+ }
+ args.AttributeS = indexes.Size() != 0
+ }
+ //ChargerProfile Indexes
+ if args.ChargerS {
+ cacheIDs[utils.CacheChargerFilterIndexes] = []string{utils.MetaAny}
+ if indexes, err = engine.ComputeIndexes(ctx, adms.dm, tnt, utils.EmptyString, utils.CacheChargerFilterIndexes,
+ nil, transactionID, func(tnt, id, grp string) (*[]string, error) {
+ ch, e := adms.dm.GetChargerProfile(ctx, tnt, id, true, false, utils.NonTransactional)
+ if e != nil {
+ return nil, e
+ }
+ return utils.SliceStringPointer(slices.Clone(ch.FilterIDs)), nil
+ }, nil); err != nil && err != utils.ErrNotFound {
+ return utils.APIErrorHandler(err)
+ }
+ args.ChargerS = indexes.Size() != 0
+ }
+ //AccountFilter Indexes
+ if args.AccountS {
+ cacheIDs[utils.CacheAccountsFilterIndexes] = []string{utils.MetaAny}
+ if indexes, err = engine.ComputeIndexes(ctx, adms.dm, tnt, utils.EmptyString, utils.CacheAccountsFilterIndexes,
+ nil, transactionID, func(tnt, id, grp string) (*[]string, error) {
+ acnts, e := adms.dm.GetAccount(ctx, tnt, id)
+ if e != nil {
+ return nil, e
+ }
+ return utils.SliceStringPointer(slices.Clone(acnts.FilterIDs)), nil
+ }, nil); err != nil && err != utils.ErrNotFound {
+ return utils.APIErrorHandler(err)
+ }
+ args.AccountS = indexes.Size() != 0
+ }
+ //ActionFilter Indexes
+ if args.ActionS {
+ cacheIDs[utils.CacheActionProfilesFilterIndexes] = []string{utils.MetaAny}
+ if indexes, err = engine.ComputeIndexes(ctx, adms.dm, tnt, utils.EmptyString, utils.CacheActionProfilesFilterIndexes,
+ nil, transactionID, func(tnt, id, grp string) (*[]string, error) {
+ act, e := adms.dm.GetActionProfile(ctx, tnt, id, true, false, utils.NonTransactional)
+ if e != nil {
+ return nil, e
+ }
+ return utils.SliceStringPointer(slices.Clone(act.FilterIDs)), nil
+ }, nil); err != nil && err != utils.ErrNotFound {
+ return utils.APIErrorHandler(err)
+ }
+ args.ActionS = indexes.Size() != 0
+ }
+ // RateFilter Indexes
+ var ratePrf []string
+ if args.RateS {
+ cacheIDs[utils.CacheRateProfilesFilterIndexes] = []string{utils.MetaAny}
+ if indexes, err = engine.ComputeIndexes(ctx, adms.dm, tnt, utils.EmptyString, utils.CacheRateProfilesFilterIndexes,
+ nil, transactionID, func(tnt, id, grp string) (*[]string, error) {
+ rtPrf, e := adms.dm.GetRateProfile(ctx, tnt, id, true, false, utils.NonTransactional)
+ if e != nil {
+ return nil, e
+ }
+ ratePrf = append(ratePrf, utils.ConcatenatedKey(tnt, id))
+ rtIds := make([]string, 0, len(rtPrf.Rates))
+ for key := range rtPrf.Rates {
+ rtIds = append(rtIds, key)
+ }
+ cacheIDs[utils.CacheRateFilterIndexes] = rtIds
+ _, e = engine.ComputeIndexes(ctx, adms.dm, tnt, id, utils.CacheRateFilterIndexes,
+ &rtIds, transactionID, func(_, id, _ string) (*[]string, error) {
+ return utils.SliceStringPointer(slices.Clone(rtPrf.Rates[id].FilterIDs)), nil
+ }, nil)
+ if e != nil {
+ return nil, e
+ }
+ return utils.SliceStringPointer(slices.Clone(rtPrf.FilterIDs)), nil
+ }, nil); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ args.RateS = indexes.Size() != 0
+ }
+ //DispatcherProfile Indexes
+ if args.DispatcherS {
+ cacheIDs[utils.CacheDispatcherFilterIndexes] = []string{utils.MetaAny}
+ if indexes, err = engine.ComputeIndexes(ctx, adms.dm, tnt, utils.EmptyString, utils.CacheDispatcherFilterIndexes,
+ nil, transactionID, func(tnt, id, grp string) (*[]string, error) {
+ dsp, e := adms.dm.GetDispatcherProfile(ctx, tnt, id, true, false, utils.NonTransactional)
+ if e != nil {
+ return nil, e
+ }
+ return utils.SliceStringPointer(slices.Clone(dsp.FilterIDs)), nil
+ }, nil); err != nil && err != utils.ErrDSPProfileNotFound {
+ return utils.APIErrorHandler(err)
+ }
+ args.DispatcherS = indexes.Size() != 0
+ }
+
+ //Now we move from tmpKey to the right key for each type
+ //ThresholdProfile Indexes
+ if args.ThresholdS {
+ if err = adms.dm.SetIndexes(ctx, utils.CacheThresholdFilterIndexes, tnt, nil, true, transactionID); err != nil {
+ return
+ }
+ }
+ //StatQueueProfile Indexes
+ if args.StatS {
+ if err = adms.dm.SetIndexes(ctx, utils.CacheStatFilterIndexes, tnt, nil, true, transactionID); err != nil {
+ return
+ }
+ }
+ //ResourceProfile Indexes
+ if args.ResourceS {
+ if err = adms.dm.SetIndexes(ctx, utils.CacheResourceFilterIndexes, tnt, nil, true, transactionID); err != nil {
+ return
+ }
+ }
+ //RouteProfile Indexes
+ if args.RouteS {
+ if err = adms.dm.SetIndexes(ctx, utils.CacheRouteFilterIndexes, tnt, nil, true, transactionID); err != nil {
+ return
+ }
+ }
+ //AttributeProfile Indexes
+ if args.AttributeS {
+ if err = adms.dm.SetIndexes(ctx, utils.CacheAttributeFilterIndexes, tnt, nil, true, transactionID); err != nil {
+ return
+ }
+ }
+ //ChargerProfile Indexes
+ if args.ChargerS {
+ if err = adms.dm.SetIndexes(ctx, utils.CacheChargerFilterIndexes, tnt, nil, true, transactionID); err != nil {
+ return
+ }
+ }
+ //AccountProfile Indexes
+ if args.AccountS {
+ if err = adms.dm.SetIndexes(ctx, utils.CacheAccountsFilterIndexes, tnt, nil, true, transactionID); err != nil {
+ return err
+ }
+ }
+ //ActionProfile Indexes
+ if args.ActionS {
+ if err = adms.dm.SetIndexes(ctx, utils.CacheActionProfilesFilterIndexes, tnt, nil, true, transactionID); err != nil {
+ return err
+ }
+ }
+ //RateProfile Indexes
+ if args.RateS {
+ if err = adms.dm.SetIndexes(ctx, utils.CacheRateProfilesFilterIndexes, tnt, nil, true, transactionID); err != nil {
+ return err
+ }
+ for _, tntId := range ratePrf {
+ if err = adms.dm.SetIndexes(ctx, utils.CacheRateFilterIndexes, tntId, nil, true, transactionID); err != nil {
+ return err
+ }
+
+ }
+ }
+ //DispatcherProfile Indexes
+ if args.DispatcherS {
+ if err = adms.dm.SetIndexes(ctx, utils.CacheDispatcherFilterIndexes, tnt, nil, true, transactionID); err != nil {
+ return
+ }
+ }
+ //generate a load
+ //ID for CacheFilterIndexes and store it in database
+ loadIDs := make(map[string]int64)
+ timeNow := time.Now().UnixNano()
+ for idx := range cacheIDs {
+ loadIDs[idx] = timeNow
+ }
+ if err := adms.dm.SetLoadIDs(ctx, loadIDs); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ if err := adms.callCacheForComputeIndexes(ctx, utils.IfaceAsString(args.APIOpts[utils.MetaCache]),
+ args.Tenant, cacheIDs, args.APIOpts); err != nil {
+ return err
+ }
+ *reply = utils.OK
+ return nil
+}
+
+// ComputeFilterIndexIDs computes specific filter indexes
+func (adms *AdminS) V1ComputeFilterIndexIDs(ctx *context.Context, args *utils.ArgsComputeFilterIndexIDs, reply *string) (err error) {
+ transactionID := utils.NonTransactional
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ var indexes utils.StringSet
+ cacheIDs := make(map[string][]string)
+ //ThresholdProfile Indexes
+ if indexes, err = engine.ComputeIndexes(ctx, adms.dm, tnt, utils.EmptyString, utils.CacheThresholdFilterIndexes,
+ &args.ThresholdIDs, transactionID, func(tnt, id, grp string) (*[]string, error) {
+ th, e := adms.dm.GetThresholdProfile(ctx, tnt, id, true, false, utils.NonTransactional)
+ if e != nil {
+ return nil, e
+ }
+ return utils.SliceStringPointer(slices.Clone(th.FilterIDs)), nil
+ }, nil); err != nil && err != utils.ErrNotFound {
+ return utils.APIErrorHandler(err)
+ }
+ if indexes.Size() != 0 {
+ cacheIDs[utils.CacheThresholdFilterIndexes] = indexes.AsSlice()
+ }
+ //StatQueueProfile Indexes
+ if indexes, err = engine.ComputeIndexes(ctx, adms.dm, tnt, utils.EmptyString, utils.CacheStatFilterIndexes,
+ &args.StatIDs, transactionID, func(tnt, id, grp string) (*[]string, error) {
+ sq, e := adms.dm.GetStatQueueProfile(ctx, tnt, id, true, false, utils.NonTransactional)
+ if e != nil {
+ return nil, e
+ }
+ cacheIDs[utils.CacheStatFilterIndexes] = []string{sq.ID}
+ return utils.SliceStringPointer(slices.Clone(sq.FilterIDs)), nil
+ }, nil); err != nil && err != utils.ErrNotFound {
+ return utils.APIErrorHandler(err)
+ }
+ if indexes.Size() != 0 {
+ cacheIDs[utils.CacheStatFilterIndexes] = indexes.AsSlice()
+ }
+ //ResourceProfile Indexes
+ if indexes, err = engine.ComputeIndexes(ctx, adms.dm, tnt, utils.EmptyString, utils.CacheResourceFilterIndexes,
+ &args.ResourceIDs, transactionID, func(tnt, id, grp string) (*[]string, error) {
+ rp, e := adms.dm.GetResourceProfile(ctx, tnt, id, true, false, utils.NonTransactional)
+ if e != nil {
+ return nil, e
+ }
+ cacheIDs[utils.CacheResourceFilterIndexes] = []string{rp.ID}
+ return utils.SliceStringPointer(slices.Clone(rp.FilterIDs)), nil
+ }, nil); err != nil && err != utils.ErrNotFound {
+ return utils.APIErrorHandler(err)
+ }
+ if indexes.Size() != 0 {
+ cacheIDs[utils.CacheResourceFilterIndexes] = indexes.AsSlice()
+ }
+ //RouteProfile Indexes
+ if indexes, err = engine.ComputeIndexes(ctx, adms.dm, tnt, utils.EmptyString, utils.CacheRouteFilterIndexes,
+ &args.RouteIDs, transactionID, func(tnt, id, grp string) (*[]string, error) {
+ rp, e := adms.dm.GetRouteProfile(ctx, tnt, id, true, false, utils.NonTransactional)
+ if e != nil {
+ return nil, e
+ }
+ cacheIDs[utils.CacheRouteFilterIndexes] = []string{rp.ID}
+ return utils.SliceStringPointer(slices.Clone(rp.FilterIDs)), nil
+ }, nil); err != nil && err != utils.ErrNotFound {
+ return utils.APIErrorHandler(err)
+ }
+ if indexes.Size() != 0 {
+ cacheIDs[utils.CacheRouteFilterIndexes] = indexes.AsSlice()
+ }
+ //AttributeProfile Indexes
+ if indexes, err = engine.ComputeIndexes(ctx, adms.dm, tnt, utils.EmptyString, utils.CacheAttributeFilterIndexes,
+ &args.AttributeIDs, transactionID, func(tnt, id, grp string) (*[]string, error) {
+ attr, e := adms.dm.GetAttributeProfile(ctx, tnt, id, true, false, utils.NonTransactional)
+ if e != nil {
+ return nil, e
+ }
+ return utils.SliceStringPointer(slices.Clone(attr.FilterIDs)), nil
+ }, nil); err != nil && err != utils.ErrNotFound {
+ return utils.APIErrorHandler(err)
+ }
+ if indexes.Size() != 0 {
+ cacheIDs[utils.CacheAttributeFilterIndexes] = indexes.AsSlice()
+ }
+ //ChargerProfile Indexes
+ if indexes, err = engine.ComputeIndexes(ctx, adms.dm, tnt, utils.EmptyString, utils.CacheChargerFilterIndexes,
+ &args.ChargerIDs, transactionID, func(tnt, id, grp string) (*[]string, error) {
+ ch, e := adms.dm.GetChargerProfile(ctx, tnt, id, true, false, utils.NonTransactional)
+ if e != nil {
+ return nil, e
+ }
+ return utils.SliceStringPointer(slices.Clone(ch.FilterIDs)), nil
+ }, nil); err != nil && err != utils.ErrNotFound {
+ return utils.APIErrorHandler(err)
+ }
+ if indexes.Size() != 0 {
+ cacheIDs[utils.CacheChargerFilterIndexes] = indexes.AsSlice()
+ }
+ //AccountIndexes
+ if indexes, err = engine.ComputeIndexes(ctx, adms.dm, tnt, utils.EmptyString, utils.CacheAccountsFilterIndexes,
+ &args.AccountIDs, transactionID, func(tnt, id, grp string) (*[]string, error) {
+ acc, e := adms.dm.GetAccount(ctx, tnt, id)
+ if e != nil {
+ return nil, e
+ }
+ return utils.SliceStringPointer(slices.Clone(acc.FilterIDs)), nil
+ }, nil); err != nil && err != utils.ErrNotFound {
+ return utils.APIErrorHandler(err)
+ }
+ if indexes.Size() != 0 {
+ cacheIDs[utils.CacheAccountsFilterIndexes] = indexes.AsSlice()
+ }
+ //ActionProfile Indexes
+ if indexes, err = engine.ComputeIndexes(ctx, adms.dm, tnt, utils.EmptyString, utils.CacheActionProfilesFilterIndexes,
+ &args.ActionProfileIDs, transactionID, func(tnt, id, grp string) (*[]string, error) {
+ act, e := adms.dm.GetActionProfile(ctx, tnt, id, true, false, utils.NonTransactional)
+ if e != nil {
+ return nil, e
+ }
+ return utils.SliceStringPointer(slices.Clone(act.FilterIDs)), nil
+ }, nil); err != nil && err != utils.ErrNotFound {
+ return utils.APIErrorHandler(err)
+ }
+ if indexes.Size() != 0 {
+ cacheIDs[utils.CacheActionProfilesFilterIndexes] = indexes.AsSlice()
+ }
+ //RateProfile Indexes
+ var ratePrf []string
+ if _, err = engine.ComputeIndexes(ctx, adms.dm, tnt, utils.EmptyString, utils.CacheRateProfilesFilterIndexes,
+ &args.RateProfileIDs, transactionID, func(tnt, id, grp string) (*[]string, error) {
+ rpr, e := adms.dm.GetRateProfile(ctx, tnt, id, true, false, utils.NonTransactional)
+ if e != nil {
+ return nil, e
+ }
+ ratePrf = append(ratePrf, utils.ConcatenatedKey(tnt, id))
+ rtIds := make([]string, 0, len(rpr.Rates))
+ for key := range rpr.Rates {
+ rtIds = append(rtIds, key)
+ }
+ indexesRate, e := engine.ComputeIndexes(ctx, adms.dm, tnt, id, utils.CacheRateFilterIndexes,
+ &rtIds, transactionID, func(_, id, _ string) (*[]string, error) {
+ return utils.SliceStringPointer(slices.Clone(rpr.Rates[id].FilterIDs)), nil
+ }, nil)
+ if e != nil {
+ return nil, e
+ }
+ if indexesRate.Size() != 0 {
+ cacheIDs[utils.CacheRateFilterIndexes] = indexesRate.AsSlice()
+ }
+ return utils.SliceStringPointer(slices.Clone(rpr.FilterIDs)), nil
+ }, nil); err != nil && err != utils.ErrNotFound {
+ return utils.APIErrorHandler(err)
+ }
+ if indexes.Size() != 0 {
+ cacheIDs[utils.CacheRateProfilesFilterIndexes] = indexes.AsSlice()
+ }
+ //DispatcherProfile Indexes
+ if indexes, err = engine.ComputeIndexes(ctx, adms.dm, tnt, utils.EmptyString, utils.CacheDispatcherFilterIndexes,
+ &args.DispatcherIDs, transactionID, func(tnt, id, grp string) (*[]string, error) {
+ dsp, e := adms.dm.GetDispatcherProfile(ctx, tnt, id, true, false, utils.NonTransactional)
+ if e != nil {
+ return nil, e
+ }
+ return utils.SliceStringPointer(slices.Clone(dsp.FilterIDs)), nil
+ }, nil); err != nil && err != utils.ErrDSPProfileNotFound {
+ return utils.APIErrorHandler(err)
+ }
+ if indexes.Size() != 0 {
+ cacheIDs[utils.CacheDispatcherFilterIndexes] = indexes.AsSlice()
+ }
+ //generate a load
+ //ID for CacheFilterIndexes and store it in database
+
+ loadIDs := make(map[string]int64)
+ timeNow := time.Now().UnixNano()
+ for idx := range cacheIDs {
+ loadIDs[idx] = timeNow
+ }
+ if err := adms.dm.SetLoadIDs(ctx, loadIDs); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ if err := adms.callCacheForComputeIndexes(ctx, utils.IfaceAsString(args.APIOpts[utils.MetaCache]),
+ args.Tenant, cacheIDs, args.APIOpts); err != nil {
+ return err
+ }
+ *reply = utils.OK
+ return nil
+}
+
+func (adms *AdminS) V1GetReverseFilterHealth(ctx *context.Context, args *engine.IndexHealthArgs, reply *map[string]*engine.ReverseFilterIHReply) (err error) {
+ objCaches := map[string]*ltcache.Cache{utils.CacheRateFilterIndexes: ltcache.NewCache(-1, 0, false, nil)}
+ for indxType := range utils.CacheIndexesToPrefix {
+ objCaches[indxType] = ltcache.NewCache(-1, 0, false, nil)
+ }
+
+ *reply, err = engine.GetRevFltrIdxHealth(ctx, adms.dm,
+ ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, nil),
+ ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, nil),
+ objCaches,
+ )
+ return
+}
+
+func (adms *AdminS) V1GetThresholdsIndexesHealth(ctx *context.Context, args *engine.IndexHealthArgs, reply *engine.FilterIHReply) error {
+ rp, err := engine.GetFltrIdxHealth(ctx, adms.dm,
+ ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, nil),
+ ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, nil),
+ ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, nil),
+ utils.CacheThresholdFilterIndexes,
+ )
+ if err != nil {
+ return err
+ }
+ *reply = *rp
+ return nil
+}
+
+func (adms *AdminS) V1GetResourcesIndexesHealth(ctx *context.Context, args *engine.IndexHealthArgs, reply *engine.FilterIHReply) error {
+ rp, err := engine.GetFltrIdxHealth(ctx, adms.dm,
+ ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, nil),
+ ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, nil),
+ ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, nil),
+ utils.CacheResourceFilterIndexes,
+ )
+ if err != nil {
+ return err
+ }
+ *reply = *rp
+ return nil
+}
+
+func (adms *AdminS) V1GetStatsIndexesHealth(ctx *context.Context, args *engine.IndexHealthArgs, reply *engine.FilterIHReply) error {
+ rp, err := engine.GetFltrIdxHealth(ctx, adms.dm,
+ ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, nil),
+ ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, nil),
+ ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, nil),
+ utils.CacheStatFilterIndexes,
+ )
+ if err != nil {
+ return err
+ }
+ *reply = *rp
+ return nil
+}
+
+func (adms *AdminS) V1GetRoutesIndexesHealth(ctx *context.Context, args *engine.IndexHealthArgs, reply *engine.FilterIHReply) error {
+ rp, err := engine.GetFltrIdxHealth(ctx, adms.dm,
+ ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, nil),
+ ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, nil),
+ ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, nil),
+ utils.CacheRouteFilterIndexes,
+ )
+ if err != nil {
+ return err
+ }
+ *reply = *rp
+ return nil
+}
+
+func (adms *AdminS) V1GetAttributesIndexesHealth(ctx *context.Context, args *engine.IndexHealthArgs, reply *engine.FilterIHReply) error {
+ rp, err := engine.GetFltrIdxHealth(ctx, adms.dm,
+ ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, nil),
+ ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, nil),
+ ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, nil),
+ utils.CacheAttributeFilterIndexes,
+ )
+ if err != nil {
+ return err
+ }
+ *reply = *rp
+ return nil
+}
+
+func (adms *AdminS) V1GetChargersIndexesHealth(ctx *context.Context, args *engine.IndexHealthArgs, reply *engine.FilterIHReply) error {
+ rp, err := engine.GetFltrIdxHealth(ctx, adms.dm,
+ ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, nil),
+ ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, nil),
+ ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, nil),
+ utils.CacheChargerFilterIndexes,
+ )
+ if err != nil {
+ return err
+ }
+ *reply = *rp
+ return nil
+}
+
+func (adms *AdminS) V1GetDispatchersIndexesHealth(ctx *context.Context, args *engine.IndexHealthArgs, reply *engine.FilterIHReply) error {
+ rp, err := engine.GetFltrIdxHealth(ctx, adms.dm,
+ ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, nil),
+ ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, nil),
+ ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, nil),
+ utils.CacheDispatcherFilterIndexes,
+ )
+ if err != nil {
+ return err
+ }
+ *reply = *rp
+ return nil
+}
+
+func (adms *AdminS) V1GetRateProfilesIndexesHealth(ctx *context.Context, args *engine.IndexHealthArgs, reply *engine.FilterIHReply) error {
+ rp, err := engine.GetFltrIdxHealth(ctx, adms.dm,
+ ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, nil),
+ ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, nil),
+ ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, nil),
+ utils.CacheRateProfilesFilterIndexes,
+ )
+ if err != nil {
+ return err
+ }
+ *reply = *rp
+ return nil
+}
+
+func (adms *AdminS) V1GetActionsIndexesHealth(ctx *context.Context, args *engine.IndexHealthArgs, reply *engine.FilterIHReply) error {
+ rp, err := engine.GetFltrIdxHealth(ctx, adms.dm,
+ ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, nil),
+ ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, nil),
+ ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, nil),
+ utils.CacheActionProfilesFilterIndexes,
+ )
+ if err != nil {
+ return err
+ }
+ *reply = *rp
+ return nil
+}
+
+func (adms *AdminS) V1GetAccountsIndexesHealth(ctx *context.Context, args *engine.IndexHealthArgs, reply *engine.FilterIHReply) error {
+ rp, err := engine.GetFltrIdxHealth(ctx, adms.dm,
+ ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, nil),
+ ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, nil),
+ ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, nil),
+ utils.CacheAccountsFilterIndexes,
+ )
+ if err != nil {
+ return err
+ }
+ *reply = *rp
+ return nil
+}
+
+func (adms *AdminS) V1GetRateRatesIndexesHealth(ctx *context.Context, args *engine.IndexHealthArgs, reply *engine.FilterIHReply) error {
+ rp, err := engine.GetFltrIdxHealthForRateRates(ctx, adms.dm,
+ ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, nil),
+ ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, nil),
+ ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, nil),
+ )
+ if err != nil {
+ return err
+ }
+ *reply = *rp
+ return nil
+}
diff --git a/admins/filters.go b/admins/filters.go
new file mode 100644
index 000000000..d11caa494
--- /dev/null
+++ b/admins/filters.go
@@ -0,0 +1,210 @@
+/*
+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 admins
+
+import (
+ "fmt"
+ "time"
+
+ "github.com/cgrates/birpc/context"
+ "github.com/cgrates/cgrates/engine"
+ "github.com/cgrates/cgrates/utils"
+)
+
+func validateFilterRules(rules []*engine.FilterRule) error {
+ for _, rule := range rules {
+ if !rule.IsValid() {
+ return fmt.Errorf("there exists at least one filter rule that is not valid")
+ }
+ }
+ return nil
+}
+
+// SetFilter add a new Filter
+func (adms *AdminS) V1SetFilter(ctx *context.Context, arg *engine.FilterWithAPIOpts, reply *string) (err error) {
+ if missing := utils.MissingStructFields(arg.Filter, []string{utils.ID}); len(missing) != 0 {
+ return utils.NewErrMandatoryIeMissing(missing...)
+ }
+ if len(arg.Rules) == 0 {
+ return utils.NewErrMandatoryIeMissing("Filter Rules")
+ }
+ if err = validateFilterRules(arg.Rules); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ if arg.Tenant == utils.EmptyString {
+ arg.Tenant = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ tntID := arg.TenantID()
+ argC := map[string][]string{utils.CacheFilters: {tntID}}
+ if fltr, err := adms.dm.GetFilter(ctx, arg.Filter.Tenant, arg.Filter.ID, true, false, utils.NonTransactional); err != nil {
+ if err != utils.ErrNotFound {
+ return utils.APIErrorHandler(err)
+ }
+ } else if argC, err = composeCacheArgsForFilter(adms.dm, ctx, fltr, fltr.Tenant, tntID, argC); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ if err := adms.dm.SetFilter(ctx, arg.Filter, true); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ if argC, err = composeCacheArgsForFilter(adms.dm, ctx, arg.Filter, arg.Filter.Tenant, tntID, argC); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //generate a loadID for CacheFilters and store it in database
+ if err := adms.dm.SetLoadIDs(ctx,
+ map[string]int64{utils.CacheFilters: time.Now().UnixNano()}); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //handle caching for Filter
+ if err := callCacheForFilter(adms.connMgr, adms.cfg.AdminSCfg().CachesConns, ctx,
+ utils.IfaceAsString(arg.APIOpts[utils.MetaCache]),
+ adms.cfg.GeneralCfg().DefaultCaching,
+ arg.Tenant, argC, arg.APIOpts); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = utils.OK
+ return
+}
+
+// GetFilter returns a Filter
+func (adms *AdminS) V1GetFilter(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *engine.Filter) 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 = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ if fltr, err := adms.dm.GetFilter(ctx, tnt, arg.ID, true, true, utils.NonTransactional); err != nil {
+ return utils.APIErrorHandler(err)
+ } else {
+ *reply = *fltr
+ }
+ return nil
+}
+
+// GetFilters returns a list of filters for a tenant
+func (adms *AdminS) V1GetFilters(ctx *context.Context, args *utils.ArgsItemIDs, fltrs *[]*engine.Filter) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ var fltrIDs []string
+ if err = adms.V1GetFilterIDs(ctx, args, &fltrIDs); err != nil {
+ return
+ }
+ *fltrs = make([]*engine.Filter, 0, len(fltrIDs))
+ for _, fltrID := range fltrIDs {
+ var fltr *engine.Filter
+ if fltr, err = adms.dm.GetFilter(ctx, tnt, fltrID, true, true, utils.NonTransactional); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *fltrs = append(*fltrs, fltr)
+ }
+ return
+}
+
+// GetFilterIDs returns list of Filter IDs registered for a tenant
+func (adms *AdminS) V1GetFilterIDs(ctx *context.Context, args *utils.ArgsItemIDs, fltrIDs *[]string) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ prfx := utils.FilterPrefix + tnt + utils.ConcatenatedKeySep
+ lenPrfx := len(prfx)
+ prfx += args.ItemsPrefix
+ var keys []string
+ if keys, err = adms.dm.DataDB().GetKeysForPrefix(ctx, prfx); err != nil {
+ return
+ }
+ if len(keys) == 0 {
+ return utils.ErrNotFound
+ }
+ retIDs := make([]string, len(keys))
+ for i, key := range keys {
+ retIDs[i] = key[lenPrfx:]
+ }
+ var limit, offset, maxItems int
+ if limit, offset, maxItems, err = utils.GetPaginateOpts(args.APIOpts); err != nil {
+ return
+ }
+ *fltrIDs, err = utils.Paginate(retIDs, limit, offset, maxItems)
+ return
+}
+
+// RemoveFilter remove a specific filter
+func (adms *AdminS) V1RemoveFilter(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, 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 = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ if err := adms.dm.RemoveFilter(ctx, tnt, arg.ID, true); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //generate a loadID for CacheFilters and store it in database
+ if err := adms.dm.SetLoadIDs(ctx, map[string]int64{utils.CacheFilters: time.Now().UnixNano()}); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //handle caching for Filter
+ if err := callCacheForFilter(adms.connMgr, adms.cfg.AdminSCfg().CachesConns, ctx,
+ utils.IfaceAsString(arg.APIOpts[utils.MetaCache]),
+ adms.cfg.GeneralCfg().DefaultCaching,
+ arg.Tenant, map[string][]string{utils.CacheFilters: {utils.ConcatenatedKey(tnt, arg.ID)}}, arg.APIOpts); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = utils.OK
+ return nil
+}
+
+// GetFiltersCount returns the total number of FilterIDs registered for a tenant
+// returns ErrNotFound in case of 0 FilterIDs
+func (admS *AdminS) V1GetFiltersCount(ctx *context.Context, args *utils.ArgsItemIDs, reply *int) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ prfx := utils.FilterPrefix + tnt + utils.ConcatenatedKeySep + args.ItemsPrefix
+ var keys []string
+ if keys, err = admS.dm.DataDB().GetKeysForPrefix(ctx, prfx); err != nil {
+ return err
+ }
+ if len(keys) == 0 {
+ return utils.ErrNotFound
+ }
+ *reply = len(keys)
+ return
+}
+
+// FiltersMatch checks whether a set of filter IDs passes for the provided CGREvent
+func (admS *AdminS) V1FiltersMatch(ctx *context.Context, args *engine.ArgsFiltersMatch, reply *bool) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ evDP := args.CGREvent.AsDataProvider()
+ var pass bool
+ if pass, err = admS.fltrS.Pass(ctx, tnt, args.FilterIDs, evDP); err != nil {
+ return
+ } else if pass {
+ *reply = true
+ }
+ return
+}
diff --git a/admins/libadmin.go b/admins/libadmin.go
new file mode 100644
index 000000000..d4703f77a
--- /dev/null
+++ b/admins/libadmin.go
@@ -0,0 +1,307 @@
+/*
+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 admins
+
+import (
+ "strings"
+
+ "github.com/cgrates/birpc/context"
+ "github.com/cgrates/cgrates/engine"
+ "github.com/cgrates/cgrates/utils"
+)
+
+// CallCache caching the item based on CacheOpt
+func (admS *AdminS) CallCache(ctx *context.Context, cacheopt string, tnt, cacheID, itemID, groupID string,
+ filters *[]string, opts map[string]any) (err error) {
+ var reply, method string
+ var args any
+ switch utils.FirstNonEmpty(cacheopt, admS.cfg.GeneralCfg().DefaultCaching) {
+ case utils.MetaNone:
+ return
+ case utils.MetaReload:
+ method = utils.CacheSv1ReloadCache
+ var argCache map[string][]string
+ if argCache, err = admS.composeArgsReload(ctx, tnt, cacheID, itemID, filters); err != nil {
+ return
+ }
+ args = utils.NewAttrReloadCacheWithOptsFromMap(argCache, tnt, opts)
+ case utils.MetaLoad:
+ method = utils.CacheSv1LoadCache
+ var argCache map[string][]string
+ if argCache, err = admS.composeArgsReload(ctx, tnt, cacheID, itemID, filters); err != nil {
+ return
+ }
+ args = utils.NewAttrReloadCacheWithOptsFromMap(argCache, tnt, opts)
+ case utils.MetaRemove:
+ if groupID != utils.EmptyString {
+ method = utils.CacheSv1RemoveGroup
+ args = &utils.ArgsGetGroupWithAPIOpts{
+ Tenant: tnt,
+ APIOpts: opts,
+ ArgsGetGroup: utils.ArgsGetGroup{
+ CacheID: cacheID,
+ GroupID: groupID,
+ },
+ }
+ break
+ }
+ method = utils.CacheSv1RemoveItems
+ var argCache map[string][]string
+ if argCache, err = admS.composeArgsReload(ctx, tnt, cacheID, itemID, filters); err != nil {
+ return
+ }
+ args = utils.NewAttrReloadCacheWithOptsFromMap(argCache, tnt, opts)
+ case utils.MetaClear:
+ cacheIDs := make([]string, 1, 3)
+ cacheIDs[0] = cacheID
+ // do not send a EmptyString if the item doesn't have indexes
+ if cIdx, has := utils.CacheInstanceToCacheIndex[cacheID]; has {
+ cacheIDs = append(cacheIDs, cIdx)
+ }
+ switch cacheID { // add the items to the cache reload
+ case utils.CacheThresholdProfiles:
+ cacheIDs = append(cacheIDs, utils.CacheThresholds)
+ case utils.CacheResourceProfiles:
+ cacheIDs = append(cacheIDs, utils.CacheResources)
+ case utils.CacheStatQueueProfiles:
+ cacheIDs = append(cacheIDs, utils.CacheStatQueues)
+ }
+ method = utils.CacheSv1Clear
+ args = &utils.AttrCacheIDsWithAPIOpts{
+ Tenant: tnt,
+ CacheIDs: cacheIDs,
+ APIOpts: opts,
+ }
+
+ }
+ return admS.connMgr.Call(ctx, admS.cfg.AdminSCfg().CachesConns,
+ method, args, &reply)
+}
+
+// composeArgsReload add the ItemID to AttrReloadCache
+// for a specific CacheID
+func (admS *AdminS) composeArgsReload(ctx *context.Context, tnt, cacheID, itemID string, filterIDs *[]string) (argCache map[string][]string, err error) {
+ argCache = map[string][]string{cacheID: {itemID}}
+ switch cacheID { // add the items to the cache reload
+ case utils.CacheThresholdProfiles:
+ argCache[utils.CacheThresholds] = []string{itemID}
+ case utils.CacheResourceProfiles:
+ argCache[utils.CacheResources] = []string{itemID}
+ case utils.CacheStatQueueProfiles:
+ argCache[utils.CacheStatQueues] = []string{itemID}
+ }
+ if filterIDs == nil { // in case we remove a profile we do not need to reload the indexes
+ return
+ }
+ // populate the indexes
+ idxCacheID := utils.CacheInstanceToCacheIndex[cacheID]
+ if len(*filterIDs) == 0 { // in case we do not have any filters reload the *none filter indexes
+ indxID := utils.ConcatenatedKey(utils.MetaNone, utils.MetaAny, utils.MetaAny)
+ argCache[idxCacheID] = []string{utils.ConcatenatedKey(tnt, indxID)}
+ return
+ }
+ indxIDs := make([]string, 0, len(*filterIDs))
+ for _, id := range *filterIDs {
+ var fltr *engine.Filter
+ if fltr, err = admS.dm.GetFilter(ctx, tnt, id, true, true, utils.NonTransactional); err != nil {
+ return
+ }
+ for _, flt := range fltr.Rules {
+ if !engine.FilterIndexTypes.Has(flt.Type) ||
+ engine.IsDynamicDPPath(flt.Element) {
+ continue
+ }
+ isDyn := strings.HasPrefix(flt.Element, utils.DynamicDataPrefix)
+ for _, fldVal := range flt.Values {
+ if engine.IsDynamicDPPath(fldVal) {
+ continue
+ }
+ if isDyn {
+ if !strings.HasPrefix(fldVal, utils.DynamicDataPrefix) {
+ indxIDs = append(indxIDs, utils.ConcatenatedKey(flt.Type, flt.Element[1:], fldVal))
+ }
+ } else if strings.HasPrefix(fldVal, utils.DynamicDataPrefix) {
+ indxIDs = append(indxIDs, utils.ConcatenatedKey(flt.Type, fldVal[1:], flt.Element))
+ }
+ }
+ }
+ }
+ argCache[idxCacheID] = make([]string, len(indxIDs))
+ for i, indxID := range indxIDs {
+ argCache[idxCacheID][i] = utils.ConcatenatedKey(tnt, indxID)
+ }
+ return
+}
+
+// callCacheForIndexes will only call CacheClear because don't have access at ItemID
+func (admS *AdminS) callCacheForRemoveIndexes(ctx *context.Context, cacheopt string, tnt, cacheID string,
+ itemIDs []string, opts map[string]any) (err error) {
+ var reply, method string
+ var args any = utils.NewAttrReloadCacheWithOptsFromMap(map[string][]string{cacheID: itemIDs}, tnt, opts)
+ switch utils.FirstNonEmpty(cacheopt, admS.cfg.GeneralCfg().DefaultCaching) {
+ case utils.MetaNone:
+ return
+ case utils.MetaReload:
+ method = utils.CacheSv1ReloadCache
+ case utils.MetaLoad:
+ method = utils.CacheSv1LoadCache
+ case utils.MetaRemove:
+ method = utils.CacheSv1RemoveItems
+ case utils.MetaClear:
+ method = utils.CacheSv1Clear
+ args = &utils.AttrCacheIDsWithAPIOpts{
+ Tenant: tnt,
+ CacheIDs: []string{cacheID},
+ APIOpts: opts,
+ }
+ }
+ return admS.connMgr.Call(ctx, admS.cfg.AdminSCfg().CachesConns,
+ method, args, &reply)
+}
+
+func (admS *AdminS) callCacheForComputeIndexes(ctx *context.Context, cacheopt, tnt string,
+ cacheItems map[string][]string, opts map[string]any) (err error) {
+ var reply, method string
+ var args any = utils.NewAttrReloadCacheWithOptsFromMap(cacheItems, tnt, opts)
+ switch utils.FirstNonEmpty(cacheopt, admS.cfg.GeneralCfg().DefaultCaching) {
+ case utils.MetaNone:
+ return
+ case utils.MetaReload:
+ method = utils.CacheSv1ReloadCache
+ case utils.MetaLoad:
+ method = utils.CacheSv1LoadCache
+ case utils.MetaRemove:
+ method = utils.CacheSv1RemoveItems
+ case utils.MetaClear:
+ method = utils.CacheSv1Clear
+ cacheIDs := make([]string, 0, len(cacheItems))
+ for idx := range cacheItems {
+ cacheIDs = append(cacheIDs, idx)
+ }
+ args = &utils.AttrCacheIDsWithAPIOpts{
+ Tenant: tnt,
+ CacheIDs: cacheIDs,
+ APIOpts: opts,
+ }
+ }
+ return admS.connMgr.Call(ctx, admS.cfg.AdminSCfg().CachesConns,
+ method, args, &reply)
+}
+
+// callCacheRevDestinations used for reverse destination, loadIDs and indexes replication
+func (admS *AdminS) callCacheMultiple(ctx *context.Context, cacheopt, tnt, cacheID string, itemIDs []string, opts map[string]any) (err error) {
+ if len(itemIDs) == 0 {
+ return
+ }
+ var reply, method string
+ var args any
+ switch utils.FirstNonEmpty(cacheopt, admS.cfg.GeneralCfg().DefaultCaching) {
+ case utils.MetaNone:
+ return
+ case utils.MetaReload:
+ method = utils.CacheSv1ReloadCache
+ args = utils.NewAttrReloadCacheWithOptsFromMap(map[string][]string{cacheID: itemIDs}, tnt, opts)
+ case utils.MetaLoad:
+ method = utils.CacheSv1LoadCache
+ args = utils.NewAttrReloadCacheWithOptsFromMap(map[string][]string{cacheID: itemIDs}, tnt, opts)
+ case utils.MetaRemove:
+ method = utils.CacheSv1RemoveItems
+ args = utils.NewAttrReloadCacheWithOptsFromMap(map[string][]string{cacheID: itemIDs}, tnt, opts)
+ case utils.MetaClear:
+ method = utils.CacheSv1Clear
+ args = &utils.AttrCacheIDsWithAPIOpts{
+ Tenant: tnt,
+ CacheIDs: []string{cacheID},
+ APIOpts: opts,
+ }
+ }
+ return admS.connMgr.Call(ctx, admS.cfg.AdminSCfg().CachesConns,
+ method, args, &reply)
+}
+
+func composeCacheArgsForFilter(dm *engine.DataManager, ctx *context.Context, fltr *engine.Filter, tnt, tntID string, args map[string][]string) (_ map[string][]string, err error) {
+ indxIDs := make([]string, 0, len(fltr.Rules))
+ for _, flt := range fltr.Rules {
+ if !engine.FilterIndexTypes.Has(flt.Type) ||
+ engine.IsDynamicDPPath(flt.Element) {
+ continue
+ }
+ isDyn := strings.HasPrefix(flt.Element, utils.DynamicDataPrefix)
+ for _, fldVal := range flt.Values {
+ if engine.IsDynamicDPPath(fldVal) {
+ continue
+ }
+ if isDyn {
+ if !strings.HasPrefix(fldVal, utils.DynamicDataPrefix) {
+ indxIDs = append(indxIDs, utils.ConcatenatedKey(flt.Type, flt.Element[1:], fldVal))
+ }
+ } else if strings.HasPrefix(fldVal, utils.DynamicDataPrefix) {
+ indxIDs = append(indxIDs, utils.ConcatenatedKey(flt.Type, fldVal[1:], flt.Element))
+ }
+ }
+ }
+ if len(indxIDs) == 0 { // no index
+ return args, nil
+ }
+
+ var rcvIndx map[string]utils.StringSet
+ if rcvIndx, err = dm.GetIndexes(ctx, utils.CacheReverseFilterIndexes, tntID,
+ utils.EmptyString, utils.EmptyString, true, true); err != nil && err != utils.ErrNotFound { // error when geting the revers
+ return
+ }
+ if err == utils.ErrNotFound || len(rcvIndx) == 0 { // no reverse index for this filter
+ return args, nil
+ }
+
+ for k := range rcvIndx {
+ for _, indx := range indxIDs {
+ args[k] = append(args[k], utils.ConcatenatedKey(tnt, indx))
+ }
+ }
+ return args, nil
+}
+
+// callCacheForFilter will call the cache for filter
+func callCacheForFilter(connMgr *engine.ConnManager, cacheConns []string, ctx *context.Context, cacheopt, dftCache, tnt string,
+ argC map[string][]string, opts map[string]any) (err error) {
+ var reply, method string
+ var args any = utils.NewAttrReloadCacheWithOptsFromMap(argC, tnt, opts)
+ switch utils.FirstNonEmpty(cacheopt, dftCache) {
+ case utils.MetaNone:
+ return
+ case utils.MetaReload:
+ method = utils.CacheSv1ReloadCache
+ case utils.MetaLoad:
+ method = utils.CacheSv1LoadCache
+ case utils.MetaRemove:
+ method = utils.CacheSv1RemoveItems
+ case utils.MetaClear:
+ cacheIDs := make([]string, 0, len(argC))
+ for k := range argC {
+ cacheIDs = append(cacheIDs, k)
+ }
+ method = utils.CacheSv1Clear
+ args = &utils.AttrCacheIDsWithAPIOpts{
+ Tenant: tnt,
+ CacheIDs: cacheIDs,
+ APIOpts: opts,
+ }
+ }
+ return connMgr.Call(ctx, cacheConns, method, args, &reply)
+}
diff --git a/admins/rates.go b/admins/rates.go
new file mode 100644
index 000000000..9197f20c7
--- /dev/null
+++ b/admins/rates.go
@@ -0,0 +1,292 @@
+/*
+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 admins
+
+import (
+ "sort"
+ "strings"
+ "time"
+
+ "github.com/cgrates/birpc/context"
+
+ "github.com/cgrates/cgrates/utils"
+)
+
+// GetRateProfile returns a Rate Profile based on tenant and id
+func (admS *AdminS) V1GetRateProfile(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *utils.RateProfile) (err 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 = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ var rPrf *utils.RateProfile
+ rPrf, err = admS.dm.GetRateProfile(ctx, tnt, arg.ID, true, true, utils.NonTransactional)
+ if err != nil {
+ if err.Error() != utils.ErrNotFound.Error() {
+ err = utils.NewErrServerError(err)
+ }
+ return
+ }
+ rateIDs := make([]string, 0, len(rPrf.Rates))
+ prefix := utils.IfaceAsString(arg.APIOpts[utils.ItemsPrefixOpt])
+ for rateID := range rPrf.Rates {
+ if strings.HasPrefix(rateID, prefix) {
+ rateIDs = append(rateIDs, rateID)
+ }
+ }
+ sort.Strings(rateIDs)
+ var limit, offset, maxItems int
+ if limit, offset, maxItems, err = utils.GetPaginateOpts(arg.APIOpts); err != nil {
+ return
+ }
+ rateIDs, err = utils.Paginate(rateIDs, limit, offset, maxItems)
+ if err != nil {
+ return
+ }
+ paginatedRatePrf := &utils.RateProfile{
+ Tenant: rPrf.Tenant,
+ ID: rPrf.ID,
+ FilterIDs: rPrf.FilterIDs,
+ Weights: rPrf.Weights,
+ MinCost: rPrf.MinCost,
+ MaxCost: rPrf.MaxCost,
+ MaxCostStrategy: rPrf.MaxCostStrategy,
+ }
+ paginatedRatePrf.Rates = make(map[string]*utils.Rate)
+ for _, rateID := range rateIDs {
+ paginatedRatePrf.Rates[rateID] = rPrf.Rates[rateID].Clone()
+ }
+ *reply = *paginatedRatePrf
+ return
+}
+
+// GetRateProfile returns the rates of a profile based on their profile. Those rates will be returned back by matching a prefix.
+func (admS *AdminS) V1GetRateProfileRates(ctx *context.Context, args *utils.ArgsSubItemIDs, reply *[]*utils.Rate) (err error) {
+ if missing := utils.MissingStructFields(args, []string{utils.ProfileID}); len(missing) != 0 { //Params missing
+ return utils.NewErrMandatoryIeMissing(missing...)
+ }
+ if args.Tenant == utils.EmptyString {
+ args.Tenant = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ _, rates, err := admS.dm.GetRateProfileRates(ctx, args, false)
+ if err != nil {
+ return
+ }
+ if len(rates) == 0 {
+ return utils.ErrNotFound
+ }
+ *reply = rates
+ return
+}
+
+// GetRateProfileIDs returns a list of rate profile IDs registered for a tenant
+func (admS *AdminS) V1GetRateProfileIDs(ctx *context.Context, args *utils.ArgsItemIDs, ratePrfIDs *[]string) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ prfx := utils.RateProfilePrefix + tnt + utils.ConcatenatedKeySep
+ lenPrfx := len(prfx)
+ prfx += args.ItemsPrefix
+ var keys []string
+ if keys, err = admS.dm.DataDB().GetKeysForPrefix(ctx, prfx); err != nil {
+ return
+ }
+ if len(keys) == 0 {
+ return utils.ErrNotFound
+ }
+ retIDs := make([]string, len(keys))
+ for i, key := range keys {
+ retIDs[i] = key[lenPrfx:]
+ }
+ var limit, offset, maxItems int
+ if limit, offset, maxItems, err = utils.GetPaginateOpts(args.APIOpts); err != nil {
+ return
+ }
+ *ratePrfIDs, err = utils.Paginate(retIDs, limit, offset, maxItems)
+ return
+}
+
+// GetRateProfileRateIDs returns a list of rates from a specific RateProfile registered for a tenant. RateIDs are returned back by matching a pattern given by ItemPrefix. If the ItemPrefix is not there, it will be returned all RateIDs.
+func (admS *AdminS) V1GetRateProfileRateIDs(ctx *context.Context, args *utils.ArgsSubItemIDs, rateIDs *[]string) (err error) {
+ if missing := utils.MissingStructFields(args, []string{utils.ProfileID}); len(missing) != 0 { //Params missing
+ return utils.NewErrMandatoryIeMissing(missing...)
+ }
+ if args.Tenant == utils.EmptyString {
+ args.Tenant = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ var ids []string
+ ids, _, err = admS.dm.GetRateProfileRates(ctx, args, true)
+ if err != nil {
+ return
+ }
+ if len(ids) == 0 {
+ return utils.ErrNotFound
+ }
+ var limit, offset, maxItems int
+ if limit, offset, maxItems, err = utils.GetPaginateOpts(args.APIOpts); err != nil {
+ return
+ }
+ *rateIDs, err = utils.Paginate(ids, limit, offset, maxItems)
+ return
+}
+
+// GetRateProfiles returns a list of rate profiles registered for a tenant
+func (admS *AdminS) V1GetRateProfiles(ctx *context.Context, args *utils.ArgsItemIDs, ratePrfs *[]*utils.RateProfile) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ var ratePrfIDs []string
+ if err = admS.V1GetRateProfileIDs(ctx, args, &ratePrfIDs); err != nil {
+ return
+ }
+ *ratePrfs = make([]*utils.RateProfile, 0, len(ratePrfIDs))
+ for _, ratePrfID := range ratePrfIDs {
+ var ratePrf *utils.RateProfile
+ ratePrf, err = admS.dm.GetRateProfile(ctx, tnt, ratePrfID, true, true, utils.NonTransactional)
+ if err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *ratePrfs = append(*ratePrfs, ratePrf)
+ }
+ return
+}
+
+// GetRateProfilesCount returns the total number of RateProfileIDs registered for a tenant
+// returns ErrNotFound in case of 0 RateProfileIDs
+func (admS *AdminS) V1GetRateProfilesCount(ctx *context.Context, args *utils.ArgsItemIDs, reply *int) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ prfx := utils.RateProfilePrefix + tnt + utils.ConcatenatedKeySep + args.ItemsPrefix
+ var keys []string
+ if keys, err = admS.dm.DataDB().GetKeysForPrefix(ctx, prfx); err != nil {
+ return err
+ }
+ if len(keys) == 0 {
+ return utils.ErrNotFound
+ }
+ *reply = len(keys)
+ return
+}
+
+// GetRateProfileRatesCount count the rates from a specific RateProfile registered for a tenant. The number of rates is returned back by matching a pattern given by ItemPrefix. If the ItemPrefix is not there, it will be counted all the rates.
+func (admS *AdminS) V1GetRateProfileRatesCount(ctx *context.Context, args *utils.ArgsSubItemIDs, countIDs *int) (err error) {
+ if missing := utils.MissingStructFields(args, []string{utils.ProfileID}); len(missing) != 0 { //Params missing
+ return utils.NewErrMandatoryIeMissing(missing...)
+ }
+ if args.Tenant == utils.EmptyString {
+ args.Tenant = admS.cfg.GeneralCfg().DefaultTenant
+ }
+
+ var ids []string
+ ids, _, err = admS.dm.GetRateProfileRates(ctx, args, true)
+ if err != nil {
+ return
+ }
+ if len(ids) == 0 {
+ return utils.ErrNotFound
+ }
+ *countIDs = len(ids)
+ return
+}
+
+// SetRateProfile add/update a new Rate Profile
+func (admS *AdminS) V1SetRateProfile(ctx *context.Context, args *utils.APIRateProfile, reply *string) (err error) {
+ if missing := utils.MissingStructFields(args, []string{utils.ID, utils.Rates}); len(missing) != 0 {
+ return utils.NewErrMandatoryIeMissing(missing...)
+ }
+ if args.Tenant == utils.EmptyString {
+ args.Tenant = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ // check if we want to overwrite our profile already existing in database
+ var optOverwrite bool
+ if _, has := args.APIOpts[utils.MetaRateSOverwrite]; has {
+ optOverwrite, err = utils.IfaceAsBool(args.APIOpts[utils.MetaRateSOverwrite])
+ if err != nil {
+ return
+ }
+ }
+ if err := admS.dm.SetRateProfile(ctx, args.RateProfile, optOverwrite, true); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //generate a loadID for CacheRateProfiles and store it in database
+ if err := admS.dm.SetLoadIDs(ctx, map[string]int64{utils.CacheRateProfiles: time.Now().UnixNano()}); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ if err := admS.CallCache(ctx, utils.IfaceAsString(args.APIOpts[utils.MetaCache]), args.Tenant, utils.CacheRateProfiles,
+ args.TenantID(), utils.EmptyString, &args.FilterIDs, args.APIOpts); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = utils.OK
+ return nil
+}
+
+// RemoveRateProfileRates removes the rates from the Rate Profile
+func (admS *AdminS) V1RemoveRateProfileRates(ctx *context.Context, args *utils.RemoveRPrfRates, reply *string) (err error) {
+ if missing := utils.MissingStructFields(args, []string{utils.ID}); len(missing) != 0 {
+ return utils.NewErrMandatoryIeMissing(missing...)
+ }
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ if err := admS.dm.RemoveRateProfileRates(ctx, tnt, args.ID, &args.RateIDs, true); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //generate a loadID for CacheRateProfiles and store it in database
+ if err := admS.dm.SetLoadIDs(ctx, map[string]int64{utils.CacheRateProfiles: time.Now().UnixNano()}); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ if err := admS.CallCache(ctx, utils.IfaceAsString(args.APIOpts[utils.MetaCache]), tnt, utils.CacheRateProfiles,
+ utils.ConcatenatedKey(tnt, args.ID), utils.EmptyString, nil, args.APIOpts); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = utils.OK
+ return nil
+}
+
+// RemoveRateProfile remove a specific Rate Profile specified by tenant and id
+func (admS *AdminS) V1RemoveRateProfile(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, 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 = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ if err := admS.dm.RemoveRateProfile(ctx, tnt, arg.ID,
+ true); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //generate a loadID for CacheAttributeProfiles and store it in database
+ if err := admS.dm.SetLoadIDs(ctx, map[string]int64{utils.CacheRateProfiles: time.Now().UnixNano()}); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ if err := admS.CallCache(ctx, utils.IfaceAsString(arg.APIOpts[utils.MetaCache]), tnt, utils.CacheRateProfiles,
+ utils.ConcatenatedKey(tnt, arg.ID), utils.EmptyString, nil, arg.APIOpts); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = utils.OK
+ return nil
+}
diff --git a/admins/resources.go b/admins/resources.go
new file mode 100644
index 000000000..fbc5a51b5
--- /dev/null
+++ b/admins/resources.go
@@ -0,0 +1,168 @@
+/*
+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 admins
+
+import (
+ "time"
+
+ "github.com/cgrates/birpc/context"
+ "github.com/cgrates/cgrates/engine"
+ "github.com/cgrates/cgrates/utils"
+)
+
+// GetResourceProfile returns a resource configuration
+func (adms *AdminS) V1GetResourceProfile(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *engine.ResourceProfile) 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 = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ if rcfg, err := adms.dm.GetResourceProfile(ctx, tnt, arg.ID, true, true, utils.NonTransactional); err != nil {
+ return utils.APIErrorHandler(err)
+ } else {
+ *reply = *rcfg
+ }
+ return nil
+}
+
+// GetResourceProfileIDs returns list of resourceProfile IDs registered for a tenant
+func (adms *AdminS) V1GetResourceProfileIDs(ctx *context.Context, args *utils.ArgsItemIDs, rsPrfIDs *[]string) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ prfx := utils.ResourceProfilesPrefix + tnt + utils.ConcatenatedKeySep
+ lenPrfx := len(prfx)
+ prfx += args.ItemsPrefix
+ var keys []string
+ if keys, err = adms.dm.DataDB().GetKeysForPrefix(ctx, prfx); err != nil {
+ return
+ }
+ if len(keys) == 0 {
+ return utils.ErrNotFound
+ }
+ retIDs := make([]string, len(keys))
+ for i, key := range keys {
+ retIDs[i] = key[lenPrfx:]
+ }
+ var limit, offset, maxItems int
+ if limit, offset, maxItems, err = utils.GetPaginateOpts(args.APIOpts); err != nil {
+ return
+ }
+ *rsPrfIDs, err = utils.Paginate(retIDs, limit, offset, maxItems)
+ return
+}
+
+// GetResourceProfiles returns a list of resource profiles registered for a tenant
+func (admS *AdminS) V1GetResourceProfiles(ctx *context.Context, args *utils.ArgsItemIDs, rsPrfs *[]*engine.ResourceProfile) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ var rsPrfIDs []string
+ if err = admS.V1GetResourceProfileIDs(ctx, args, &rsPrfIDs); err != nil {
+ return
+ }
+ *rsPrfs = make([]*engine.ResourceProfile, 0, len(rsPrfIDs))
+ for _, rsPrfID := range rsPrfIDs {
+ var rsPrf *engine.ResourceProfile
+ rsPrf, err = admS.dm.GetResourceProfile(ctx, tnt, rsPrfID, true, true, utils.NonTransactional)
+ if err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *rsPrfs = append(*rsPrfs, rsPrf)
+ }
+ return
+}
+
+// GetResourceProfilesCount returns the total number of ResourceProfileIDs registered for a tenant
+// returns ErrNotFound in case of 0 ResourceProfileIDs
+func (admS *AdminS) V1GetResourceProfilesCount(ctx *context.Context, args *utils.ArgsItemIDs, reply *int) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ prfx := utils.ResourceProfilesPrefix + tnt + utils.ConcatenatedKeySep + args.ItemsPrefix
+ var keys []string
+ if keys, err = admS.dm.DataDB().GetKeysForPrefix(ctx, prfx); err != nil {
+ return err
+ }
+ if len(keys) == 0 {
+ return utils.ErrNotFound
+ }
+ *reply = len(keys)
+ return
+}
+
+// SetResourceProfile adds a new resource configuration
+func (adms *AdminS) V1SetResourceProfile(ctx *context.Context, arg *engine.ResourceProfileWithAPIOpts, reply *string) (err error) {
+ if missing := utils.MissingStructFields(arg.ResourceProfile, []string{utils.ID}); len(missing) != 0 {
+ return utils.NewErrMandatoryIeMissing(missing...)
+ }
+ if arg.Tenant == utils.EmptyString {
+ arg.Tenant = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ if err = adms.dm.SetResourceProfile(ctx, arg.ResourceProfile, true); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //generate a loadID for CacheResourceProfiles and CacheResources and store it in database
+ //make 1 insert for both ResourceProfile and Resources instead of 2
+ loadID := time.Now().UnixNano()
+ if err = adms.dm.SetLoadIDs(ctx,
+ map[string]int64{utils.CacheResourceProfiles: loadID,
+ utils.CacheResources: loadID}); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //handle caching for ResourceProfile
+ if err = adms.CallCache(ctx, utils.IfaceAsString(arg.APIOpts[utils.MetaCache]), arg.Tenant, utils.CacheResourceProfiles,
+ arg.TenantID(), utils.EmptyString, &arg.FilterIDs, arg.APIOpts); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = utils.OK
+ return nil
+}
+
+// RemoveResourceProfile remove a specific resource configuration
+func (adms *AdminS) V1RemoveResourceProfile(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, 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 = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ if err := adms.dm.RemoveResourceProfile(ctx, tnt, arg.ID, true); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //handle caching for ResourceProfile
+ if err := adms.CallCache(ctx, utils.IfaceAsString(arg.APIOpts[utils.MetaCache]), tnt, utils.CacheResourceProfiles,
+ utils.ConcatenatedKey(tnt, arg.ID), utils.EmptyString, nil, arg.APIOpts); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //generate a loadID for CacheResourceProfiles and CacheResources and store it in database
+ //make 1 insert for both ResourceProfile and Resources instead of 2
+ loadID := time.Now().UnixNano()
+ if err := adms.dm.SetLoadIDs(ctx, map[string]int64{utils.CacheResourceProfiles: loadID, utils.CacheResources: loadID}); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = utils.OK
+ return nil
+}
diff --git a/admins/routes.go b/admins/routes.go
new file mode 100644
index 000000000..6a12da3f8
--- /dev/null
+++ b/admins/routes.go
@@ -0,0 +1,165 @@
+/*
+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 admins
+
+import (
+ "time"
+
+ "github.com/cgrates/birpc/context"
+ "github.com/cgrates/cgrates/engine"
+ "github.com/cgrates/cgrates/utils"
+)
+
+// GetRouteProfile returns a Route configuration
+func (adms *AdminS) V1GetRouteProfile(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *engine.RouteProfile) 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 = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ if rp, err := adms.dm.GetRouteProfile(ctx, tnt, arg.ID, true, true, utils.NonTransactional); err != nil {
+ return utils.APIErrorHandler(err)
+ } else {
+ *reply = *rp
+ }
+ return nil
+}
+
+// GetRouteProfileIDs returns list of routeProfile IDs registered for a tenant
+func (adms *AdminS) V1GetRouteProfileIDs(ctx *context.Context, args *utils.ArgsItemIDs, routeProfileIDs *[]string) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ prfx := utils.RouteProfilePrefix + tnt + utils.ConcatenatedKeySep
+ lenPrfx := len(prfx)
+ prfx += args.ItemsPrefix
+ var keys []string
+ if keys, err = adms.dm.DataDB().GetKeysForPrefix(ctx, prfx); err != nil {
+ return
+ }
+ if len(keys) == 0 {
+ return utils.ErrNotFound
+ }
+ retIDs := make([]string, len(keys))
+ for i, key := range keys {
+ retIDs[i] = key[lenPrfx:]
+ }
+ var limit, offset, maxItems int
+ if limit, offset, maxItems, err = utils.GetPaginateOpts(args.APIOpts); err != nil {
+ return
+ }
+ *routeProfileIDs, err = utils.Paginate(retIDs, limit, offset, maxItems)
+ return
+}
+
+// GetRouteProfiles returns a list of route profiles registered for a tenant
+func (admS *AdminS) V1GetRouteProfiles(ctx *context.Context, args *utils.ArgsItemIDs, rouPrfs *[]*engine.RouteProfile) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ var rouPrfIDs []string
+ if err = admS.V1GetRouteProfileIDs(ctx, args, &rouPrfIDs); err != nil {
+ return
+ }
+ *rouPrfs = make([]*engine.RouteProfile, 0, len(rouPrfIDs))
+ for _, rouPrfID := range rouPrfIDs {
+ var rouPrf *engine.RouteProfile
+ rouPrf, err = admS.dm.GetRouteProfile(ctx, tnt, rouPrfID, true, true, utils.NonTransactional)
+ if err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *rouPrfs = append(*rouPrfs, rouPrf)
+ }
+ return
+}
+
+// GetRouteProfilesCount sets in reply var the total number of RouteProfileIDs registered for the received tenant
+// returns ErrNotFound in case of 0 RouteProfileIDs
+func (adms *AdminS) V1GetRouteProfilesCount(ctx *context.Context, args *utils.ArgsItemIDs, reply *int) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ prfx := utils.RouteProfilePrefix + tnt + utils.ConcatenatedKeySep + args.ItemsPrefix
+ var keys []string
+ if keys, err = adms.dm.DataDB().GetKeysForPrefix(ctx, prfx); err != nil {
+ return err
+ }
+ if len(keys) == 0 {
+ return utils.ErrNotFound
+ }
+ *reply = len(keys)
+ return
+}
+
+// SetRouteProfile add a new Route configuration
+func (adms *AdminS) V1SetRouteProfile(ctx *context.Context, args *engine.RouteProfileWithAPIOpts, reply *string) error {
+ if missing := utils.MissingStructFields(args.RouteProfile, []string{utils.ID}); len(missing) != 0 {
+ return utils.NewErrMandatoryIeMissing(missing...)
+ }
+ if args.Tenant == utils.EmptyString {
+ args.Tenant = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ if len(args.Routes) == 0 {
+ return utils.NewErrMandatoryIeMissing("Routes")
+ }
+ if err := adms.dm.SetRouteProfile(ctx, args.RouteProfile, true); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //generate a loadID for CacheRouteProfiles and store it in database
+ if err := adms.dm.SetLoadIDs(ctx, map[string]int64{utils.CacheRouteProfiles: time.Now().UnixNano()}); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //handle caching for SupplierProfile
+ if err := adms.CallCache(ctx, utils.IfaceAsString(args.APIOpts[utils.MetaCache]), args.Tenant, utils.CacheRouteProfiles,
+ args.TenantID(), utils.EmptyString, &args.FilterIDs, args.APIOpts); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = utils.OK
+ return nil
+}
+
+// RemoveRouteProfile remove a specific Route configuration
+func (adms *AdminS) V1RemoveRouteProfile(ctx *context.Context, args *utils.TenantIDWithAPIOpts, reply *string) error {
+ if missing := utils.MissingStructFields(args, []string{utils.ID}); len(missing) != 0 { //Params missing
+ return utils.NewErrMandatoryIeMissing(missing...)
+ }
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ if err := adms.dm.RemoveRouteProfile(ctx, tnt, args.ID, true); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //generate a loadID for CacheRouteProfiles and store it in database
+ if err := adms.dm.SetLoadIDs(ctx, map[string]int64{utils.CacheRouteProfiles: time.Now().UnixNano()}); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //handle caching for SupplierProfile
+ if err := adms.CallCache(ctx, utils.IfaceAsString(args.APIOpts[utils.MetaCache]), tnt, utils.CacheRouteProfiles,
+ utils.ConcatenatedKey(tnt, args.ID), utils.EmptyString, nil, args.APIOpts); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = utils.OK
+ return nil
+}
diff --git a/admins/stats.go b/admins/stats.go
new file mode 100644
index 000000000..d72a1fa7d
--- /dev/null
+++ b/admins/stats.go
@@ -0,0 +1,166 @@
+/*
+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 admins
+
+import (
+ "time"
+
+ "github.com/cgrates/birpc/context"
+ "github.com/cgrates/cgrates/engine"
+ "github.com/cgrates/cgrates/utils"
+)
+
+// GetStatQueueProfile returns a StatQueue profile
+func (adms *AdminS) V1GetStatQueueProfile(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *engine.StatQueueProfile) (err 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 = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ sCfg, err := adms.dm.GetStatQueueProfile(ctx, tnt, arg.ID,
+ true, true, utils.NonTransactional)
+ if err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = *sCfg
+ return
+}
+
+// GetStatQueueProfileIDs returns list of statQueueProfile IDs registered for a tenant
+func (adms *AdminS) V1GetStatQueueProfileIDs(ctx *context.Context, args *utils.ArgsItemIDs, stsPrfIDs *[]string) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ prfx := utils.StatQueueProfilePrefix + tnt + utils.ConcatenatedKeySep
+ lenPrfx := len(prfx)
+ prfx += args.ItemsPrefix
+ var keys []string
+ if keys, err = adms.dm.DataDB().GetKeysForPrefix(ctx, prfx); err != nil {
+ return
+ }
+ if len(keys) == 0 {
+ return utils.ErrNotFound
+ }
+ retIDs := make([]string, len(keys))
+ for i, key := range keys {
+ retIDs[i] = key[lenPrfx:]
+ }
+ var limit, offset, maxItems int
+ if limit, offset, maxItems, err = utils.GetPaginateOpts(args.APIOpts); err != nil {
+ return
+ }
+ *stsPrfIDs, err = utils.Paginate(retIDs, limit, offset, maxItems)
+ return
+}
+
+// GetStatQueueProfiles returns a list of stats profiles registered for a tenant
+func (admS *AdminS) V1GetStatQueueProfiles(ctx *context.Context, args *utils.ArgsItemIDs, sqPrfs *[]*engine.StatQueueProfile) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ var sqPrfIDs []string
+ if err = admS.V1GetStatQueueProfileIDs(ctx, args, &sqPrfIDs); err != nil {
+ return
+ }
+ *sqPrfs = make([]*engine.StatQueueProfile, 0, len(sqPrfIDs))
+ for _, sqPrfID := range sqPrfIDs {
+ var sqPrf *engine.StatQueueProfile
+ sqPrf, err = admS.dm.GetStatQueueProfile(ctx, tnt, sqPrfID, true, true, utils.NonTransactional)
+ if err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *sqPrfs = append(*sqPrfs, sqPrf)
+ }
+ return
+}
+
+// GetStatQueueProfilesCount returns the total number of StatQueueProfileIDs registered for a tenant
+// returns ErrNotFound in case of 0 StatQueueProfileIDs
+func (admS *AdminS) V1GetStatQueueProfilesCount(ctx *context.Context, args *utils.ArgsItemIDs, reply *int) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ prfx := utils.StatQueueProfilePrefix + tnt + utils.ConcatenatedKeySep + args.ItemsPrefix
+ var keys []string
+ if keys, err = admS.dm.DataDB().GetKeysForPrefix(ctx, prfx); err != nil {
+ return err
+ }
+ if len(keys) == 0 {
+ return utils.ErrNotFound
+ }
+ *reply = len(keys)
+ return
+}
+
+// SetStatQueueProfile alters/creates a StatQueueProfile
+func (adms *AdminS) V1SetStatQueueProfile(ctx *context.Context, arg *engine.StatQueueProfileWithAPIOpts, reply *string) (err error) {
+ if missing := utils.MissingStructFields(arg.StatQueueProfile, []string{utils.ID}); len(missing) != 0 {
+ return utils.NewErrMandatoryIeMissing(missing...)
+ }
+ if arg.Tenant == utils.EmptyString {
+ arg.Tenant = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ if err = adms.dm.SetStatQueueProfile(ctx, arg.StatQueueProfile, true); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //generate a loadID for CacheStatQueueProfiles and CacheStatQueues and store it in database
+ //make 1 insert for both StatQueueProfile and StatQueue instead of 2
+ loadID := time.Now().UnixNano()
+ if err = adms.dm.SetLoadIDs(ctx, map[string]int64{utils.CacheStatQueueProfiles: loadID, utils.CacheStatQueues: loadID}); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //handle caching for StatQueueProfile
+ if err = adms.CallCache(ctx, utils.IfaceAsString(arg.APIOpts[utils.MetaCache]), arg.Tenant, utils.CacheStatQueueProfiles,
+ arg.TenantID(), utils.EmptyString, &arg.FilterIDs, arg.APIOpts); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = utils.OK
+ return nil
+}
+
+// RemoveStatQueueProfile remove a specific stat configuration
+func (adms *AdminS) V1RemoveStatQueueProfile(ctx *context.Context, args *utils.TenantIDWithAPIOpts, reply *string) error {
+ if missing := utils.MissingStructFields(args, []string{utils.ID}); len(missing) != 0 { //Params missing
+ return utils.NewErrMandatoryIeMissing(missing...)
+ }
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ if err := adms.dm.RemoveStatQueueProfile(ctx, tnt, args.ID, true); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //handle caching for StatQueueProfile
+ if err := adms.CallCache(ctx, utils.IfaceAsString(args.APIOpts[utils.MetaCache]), tnt, utils.CacheStatQueueProfiles,
+ utils.ConcatenatedKey(tnt, args.ID), utils.EmptyString, nil, args.APIOpts); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //generate a loadID for CacheStatQueueProfiles and CacheStatQueues and store it in database
+ //make 1 insert for both StatQueueProfile and StatQueue instead of 2
+ loadID := time.Now().UnixNano()
+ if err := adms.dm.SetLoadIDs(ctx, map[string]int64{utils.CacheStatQueueProfiles: loadID, utils.CacheStatQueues: loadID}); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = utils.OK
+ return nil
+}
diff --git a/admins/thresholds.go b/admins/thresholds.go
new file mode 100644
index 000000000..f59c168a2
--- /dev/null
+++ b/admins/thresholds.go
@@ -0,0 +1,166 @@
+/*
+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 admins
+
+import (
+ "time"
+
+ "github.com/cgrates/birpc/context"
+ "github.com/cgrates/cgrates/engine"
+ "github.com/cgrates/cgrates/utils"
+)
+
+// GetThresholdProfile returns a Threshold Profile
+func (adms *AdminS) V1GetThresholdProfile(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *engine.ThresholdProfile) (err 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 = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ th, err := adms.dm.GetThresholdProfile(ctx, tnt, arg.ID, true, true, utils.NonTransactional)
+ if err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = *th
+ return
+}
+
+// GetThresholdProfileIDs returns list of thresholdProfile IDs registered for a tenant
+func (adms *AdminS) V1GetThresholdProfileIDs(ctx *context.Context, args *utils.ArgsItemIDs, thPrfIDs *[]string) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ prfx := utils.ThresholdProfilePrefix + tnt + utils.ConcatenatedKeySep
+ lenPrfx := len(prfx)
+ prfx += args.ItemsPrefix
+ var keys []string
+ if keys, err = adms.dm.DataDB().GetKeysForPrefix(ctx, prfx); err != nil {
+ return
+ }
+ if len(keys) == 0 {
+ return utils.ErrNotFound
+ }
+ retIDs := make([]string, len(keys))
+ for i, key := range keys {
+ retIDs[i] = key[lenPrfx:]
+ }
+ var limit, offset, maxItems int
+ if limit, offset, maxItems, err = utils.GetPaginateOpts(args.APIOpts); err != nil {
+ return
+ }
+ *thPrfIDs, err = utils.Paginate(retIDs, limit, offset, maxItems)
+ return
+}
+
+// GetThresholdProfiles returns a list of threshold profiles registered for a tenant
+func (admS *AdminS) V1GetThresholdProfiles(ctx *context.Context, args *utils.ArgsItemIDs, thdPrfs *[]*engine.ThresholdProfile) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = admS.cfg.GeneralCfg().DefaultTenant
+ }
+ var thdPrfIDs []string
+ if err = admS.V1GetThresholdProfileIDs(ctx, args, &thdPrfIDs); err != nil {
+ return
+ }
+ *thdPrfs = make([]*engine.ThresholdProfile, 0, len(thdPrfIDs))
+ for _, thdPrfID := range thdPrfIDs {
+ var thdPrf *engine.ThresholdProfile
+ thdPrf, err = admS.dm.GetThresholdProfile(ctx, tnt, thdPrfID, true, true, utils.NonTransactional)
+ if err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *thdPrfs = append(*thdPrfs, thdPrf)
+ }
+ return
+}
+
+// GetThresholdProfilesCount sets in reply var the total number of ThresholdProfileIDs registered for the received tenant
+// returns ErrNotFound in case of 0 ThresholdProfileIDs
+func (adms *AdminS) V1GetThresholdProfilesCount(ctx *context.Context, args *utils.ArgsItemIDs, reply *int) (err error) {
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ prfx := utils.ThresholdProfilePrefix + tnt + utils.ConcatenatedKeySep + args.ItemsPrefix
+ var keys []string
+ if keys, err = adms.dm.DataDB().GetKeysForPrefix(ctx, prfx); err != nil {
+ return err
+ }
+ if len(keys) == 0 {
+ return utils.ErrNotFound
+ }
+ *reply = len(keys)
+ return
+}
+
+// SetThresholdProfile alters/creates a ThresholdProfile
+func (adms *AdminS) V1SetThresholdProfile(ctx *context.Context, args *engine.ThresholdProfileWithAPIOpts, reply *string) error {
+ if missing := utils.MissingStructFields(args.ThresholdProfile, []string{utils.ID}); len(missing) != 0 {
+ return utils.NewErrMandatoryIeMissing(missing...)
+ }
+ if args.Tenant == utils.EmptyString {
+ args.Tenant = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ if err := adms.dm.SetThresholdProfile(ctx, args.ThresholdProfile, true); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //generate a loadID for CacheThresholdProfiles and CacheThresholds and store it in database
+ //make 1 insert for both ThresholdProfile and Threshold instead of 2
+ loadID := time.Now().UnixNano()
+ if err := adms.dm.SetLoadIDs(ctx, map[string]int64{utils.CacheThresholdProfiles: loadID, utils.CacheThresholds: loadID}); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //handle caching for ThresholdProfile and Threshold
+ if err := adms.CallCache(ctx, utils.IfaceAsString(args.APIOpts[utils.MetaCache]), args.Tenant, utils.CacheThresholdProfiles,
+ args.TenantID(), utils.EmptyString, &args.FilterIDs, args.APIOpts); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = utils.OK
+ return nil
+}
+
+// RemoveThresholdProfile removes a specific Threshold Profile
+func (adms *AdminS) V1RemoveThresholdProfile(ctx *context.Context, args *utils.TenantIDWithAPIOpts, reply *string) error {
+ if missing := utils.MissingStructFields(args, []string{utils.ID}); len(missing) != 0 { //Params missing
+ return utils.NewErrMandatoryIeMissing(missing...)
+ }
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = adms.cfg.GeneralCfg().DefaultTenant
+ }
+ if err := adms.dm.RemoveThresholdProfile(ctx, tnt, args.ID, true); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //handle caching for ThresholdProfile
+ if err := adms.CallCache(ctx, utils.IfaceAsString(args.APIOpts[utils.MetaCache]), tnt, utils.CacheThresholdProfiles,
+ utils.ConcatenatedKey(tnt, args.ID), utils.EmptyString, nil, args.APIOpts); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ //generate a loadID for CacheThresholdProfiles and CacheThresholds and store it in database
+ //make 1 insert for both ThresholdProfile and Threshold instead of 2
+ loadID := time.Now().UnixNano()
+ if err := adms.dm.SetLoadIDs(ctx, map[string]int64{utils.CacheThresholdProfiles: loadID, utils.CacheThresholds: loadID}); err != nil {
+ return utils.APIErrorHandler(err)
+ }
+ *reply = utils.OK
+ return nil
+}