Files
cgrates/apier/v1/triggers.go
2026-01-14 11:07:12 +01:00

461 lines
14 KiB
Go

/*
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 Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>
*/
package v1
import (
"strings"
"time"
"github.com/cgrates/birpc/context"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/guardian"
"github.com/cgrates/cgrates/utils"
)
// Returns a list of ActionTriggers on an account
func (apierSv1 *APIerSv1) GetAccountActionTriggers(ctx *context.Context, attrs *utils.TenantAccount, reply *engine.ActionTriggers) error {
if missing := utils.MissingStructFields(attrs, []string{utils.AccountField}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
tnt := attrs.Tenant
if tnt == utils.EmptyString {
tnt = apierSv1.Config.GeneralCfg().DefaultTenant
}
if account, err := apierSv1.DataManager.GetAccount(utils.ConcatenatedKey(tnt, attrs.Account)); err != nil {
return utils.NewErrServerError(err)
} else {
ats := account.ActionTriggers
if ats == nil {
ats = engine.ActionTriggers{}
}
*reply = ats
}
return nil
}
type AttrAddAccountActionTriggers struct {
Tenant string
Account string
ActionTriggerIDs []string
ActionTriggerOverwrite bool
ActivationDate string
Executed bool
}
func (apierSv1 *APIerSv1) AddAccountActionTriggers(ctx *context.Context, attr *AttrAddAccountActionTriggers, reply *string) (err error) {
if missing := utils.MissingStructFields(attr, []string{utils.AccountField}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
tnt := attr.Tenant
if tnt == utils.EmptyString {
tnt = apierSv1.Config.GeneralCfg().DefaultTenant
}
var actTime time.Time
if actTime, err = utils.ParseTimeDetectLayout(attr.ActivationDate,
apierSv1.Config.GeneralCfg().DefaultTimezone); err != nil {
return
}
accID := utils.ConcatenatedKey(tnt, attr.Account)
var account *engine.Account
err = guardian.Guardian.Guard(func() error {
if account, err = apierSv1.DataManager.GetAccount(accID); err != nil {
return err
}
if attr.ActionTriggerOverwrite {
account.ActionTriggers = make(engine.ActionTriggers, 0)
}
for _, actionTriggerID := range attr.ActionTriggerIDs {
atrs, err := apierSv1.DataManager.GetActionTriggers(actionTriggerID, false, utils.NonTransactional)
if err != nil {
return err
}
for _, at := range atrs {
var found bool
for _, existingAt := range account.ActionTriggers {
if existingAt.Equals(at) {
found = true
break
}
}
at.ActivationDate = actTime
at.Executed = attr.Executed
if !found {
account.ActionTriggers = append(account.ActionTriggers, at)
}
}
}
account.InitCounters()
return apierSv1.DataManager.SetAccount(account)
}, config.CgrConfig().GeneralCfg().LockingTimeout, utils.AccountPrefix+accID)
if err != nil {
return
}
*reply = utils.OK
return
}
type AttrRemoveAccountActionTriggers struct {
Tenant string
Account string
GroupID string
UniqueID string
}
func (apierSv1 *APIerSv1) RemoveAccountActionTriggers(ctx *context.Context, attr *AttrRemoveAccountActionTriggers, reply *string) error {
if missing := utils.MissingStructFields(attr, []string{utils.AccountField}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
tnt := attr.Tenant
if tnt == utils.EmptyString {
tnt = apierSv1.Config.GeneralCfg().DefaultTenant
}
accID := utils.ConcatenatedKey(tnt, attr.Account)
err := guardian.Guardian.Guard(func() error {
var account *engine.Account
if acc, err := apierSv1.DataManager.GetAccount(accID); err == nil {
account = acc
} else {
return err
}
var newActionTriggers engine.ActionTriggers
for _, at := range account.ActionTriggers {
if (attr.UniqueID == "" || at.UniqueID == attr.UniqueID) &&
(attr.GroupID == "" || at.ID == attr.GroupID) {
// remove action trigger
continue
}
newActionTriggers = append(newActionTriggers, at)
}
account.ActionTriggers = newActionTriggers
account.InitCounters()
return apierSv1.DataManager.SetAccount(account)
}, config.CgrConfig().GeneralCfg().LockingTimeout, accID)
if err != nil {
*reply = err.Error()
return err
}
*reply = utils.OK
return nil
}
type AttrResetAccountActionTriggers struct {
Tenant string
Account string
GroupID string
UniqueID string
Executed bool
}
func (apierSv1 *APIerSv1) ResetAccountActionTriggers(ctx *context.Context, attr *AttrResetAccountActionTriggers, reply *string) error {
if missing := utils.MissingStructFields(attr, []string{utils.AccountField}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
tnt := attr.Tenant
if tnt == utils.EmptyString {
tnt = apierSv1.Config.GeneralCfg().DefaultTenant
}
accID := utils.ConcatenatedKey(tnt, attr.Account)
var account *engine.Account
err := guardian.Guardian.Guard(func() error {
if acc, err := apierSv1.DataManager.GetAccount(accID); err == nil {
account = acc
} else {
return err
}
for _, at := range account.ActionTriggers {
if (attr.UniqueID == "" || at.UniqueID == attr.UniqueID) &&
(attr.GroupID == "" || at.ID == attr.GroupID) {
// reset action trigger
at.Executed = attr.Executed
}
}
if !attr.Executed {
account.ExecuteActionTriggers(nil, apierSv1.FilterS)
}
return apierSv1.DataManager.SetAccount(account)
}, config.CgrConfig().GeneralCfg().LockingTimeout, utils.AccountPrefix+accID)
if err != nil {
*reply = err.Error()
return err
}
*reply = utils.OK
return nil
}
type AttrSetAccountActionTriggers struct {
Tenant string
Account string
engine.AttrSetActionTrigger
}
// SetAccountActionTriggers updates or creates if not present the ActionTrigger for an Account
func (apierSv1 *APIerSv1) SetAccountActionTriggers(ctx *context.Context, attr *AttrSetAccountActionTriggers, reply *string) error {
if missing := utils.MissingStructFields(attr, []string{utils.AccountField}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
tnt := attr.Tenant
if tnt == utils.EmptyString {
tnt = apierSv1.Config.GeneralCfg().DefaultTenant
}
accID := utils.ConcatenatedKey(tnt, attr.Account)
var account *engine.Account
err := guardian.Guardian.Guard(func() error {
if acc, err := apierSv1.DataManager.GetAccount(accID); err == nil {
account = acc
} else {
return err
}
var foundOne bool
for _, at := range account.ActionTriggers {
if updated, err := attr.UpdateActionTrigger(at,
apierSv1.Config.GeneralCfg().DefaultTimezone); err != nil {
return err
} else if updated && !foundOne {
foundOne = true
}
}
if !foundOne { // Did not find one to update, create a new AT
at := new(engine.ActionTrigger)
if updated, err := attr.UpdateActionTrigger(at,
apierSv1.Config.GeneralCfg().DefaultTimezone); err != nil {
return err
} else if updated { // Adding a new AT
account.ActionTriggers = append(account.ActionTriggers, at)
}
}
account.ExecuteActionTriggers(nil, apierSv1.FilterS)
return apierSv1.DataManager.SetAccount(account)
}, config.CgrConfig().GeneralCfg().LockingTimeout, utils.AccountPrefix+accID)
if err != nil {
*reply = err.Error()
return err
}
*reply = utils.OK
return nil
}
type AttrRemoveActionTrigger struct {
GroupID string
UniqueID string
}
func (apierSv1 *APIerSv1) RemoveActionTrigger(ctx *context.Context, attr *AttrRemoveActionTrigger, reply *string) (err error) {
if missing := utils.MissingStructFields(attr, []string{"GroupID"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
if attr.UniqueID == "" {
err = apierSv1.DataManager.RemoveActionTriggers(attr.GroupID, utils.NonTransactional)
if err != nil {
return
}
*reply = utils.OK
return
}
var atrs engine.ActionTriggers
if atrs, err = apierSv1.DataManager.GetActionTriggers(attr.GroupID, false, utils.NonTransactional); err != nil {
return
}
remainingAtrs := make(engine.ActionTriggers, 0, len(atrs))
for _, atr := range atrs {
if atr.UniqueID != attr.UniqueID {
remainingAtrs = append(remainingAtrs, atr)
}
}
// set the cleared list back
if err = apierSv1.DataManager.SetActionTriggers(attr.GroupID, remainingAtrs); err != nil {
return
}
// CacheReload
if err = apierSv1.ConnMgr.Call(context.TODO(), apierSv1.Config.ApierCfg().CachesConns,
utils.CacheSv1ReloadCache, &utils.AttrReloadCacheWithAPIOpts{
ActionTriggerIDs: []string{attr.GroupID},
}, reply); err != nil {
return
}
// generate a loadID for CacheActionTriggers and store it in database
if err = apierSv1.DataManager.SetLoadIDs(map[string]int64{utils.CacheActionTriggers: time.Now().UnixNano()}); err != nil {
return utils.APIErrorHandler(err)
}
*reply = utils.OK
return
}
// SetActionTrigger updates a ActionTrigger
func (apierSv1 *APIerSv1) SetActionTrigger(ctx *context.Context, attr *engine.AttrSetActionTrigger, reply *string) (err error) {
if missing := utils.MissingStructFields(attr, []string{"GroupID"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
atrs, _ := apierSv1.DataManager.GetActionTriggers(attr.GroupID, false, utils.NonTransactional)
var newAtr *engine.ActionTrigger
if attr.UniqueID != utils.EmptyString {
//search for exiting one
for _, atr := range atrs {
if atr.UniqueID == attr.UniqueID {
newAtr = atr
break
}
}
}
if newAtr == nil {
newAtr = &engine.ActionTrigger{}
atrs = append(atrs, newAtr)
}
if attr.UniqueID == utils.EmptyString {
attr.UniqueID = utils.GenUUID()
}
newAtr.ID = attr.GroupID
newAtr.UniqueID = attr.UniqueID
if _, err = attr.UpdateActionTrigger(newAtr,
apierSv1.Config.GeneralCfg().DefaultTimezone); err != nil {
return
}
if err = apierSv1.DataManager.SetActionTriggers(attr.GroupID, atrs); err != nil {
return
}
// CacheReload
if err = apierSv1.ConnMgr.Call(context.TODO(), apierSv1.Config.ApierCfg().CachesConns,
utils.CacheSv1ReloadCache, &utils.AttrReloadCacheWithAPIOpts{
ActionTriggerIDs: []string{attr.GroupID},
}, reply); err != nil {
return
}
// generate a loadID for CacheActionTriggers and store it in database
if err = apierSv1.DataManager.SetLoadIDs(map[string]int64{utils.CacheActionTriggers: time.Now().UnixNano()}); err != nil {
return utils.APIErrorHandler(err)
}
*reply = utils.OK
return
}
type AttrGetActionTriggers struct {
GroupIDs []string
}
func (apierSv1 *APIerSv1) GetActionTriggers(ctx *context.Context, attr *AttrGetActionTriggers, atrs *engine.ActionTriggers) error {
var allAttrs engine.ActionTriggers
if len(attr.GroupIDs) > 0 {
for _, key := range attr.GroupIDs {
getAttrs, err := apierSv1.DataManager.GetActionTriggers(key, false, utils.NonTransactional)
if err != nil {
return err
}
allAttrs = append(allAttrs, getAttrs...)
}
} else {
keys, err := apierSv1.DataManager.DataDB().GetKeysForPrefix(utils.ActionTriggerPrefix, utils.EmptyString)
if err != nil {
return err
}
if len(keys) == 0 {
return utils.ErrNotFound
}
for _, key := range keys {
getAttrs, err := apierSv1.DataManager.GetActionTriggers(key[len(utils.ActionTriggerPrefix):], false, utils.NonTransactional)
if err != nil {
return err
}
allAttrs = append(allAttrs, getAttrs...)
}
}
*atrs = allAttrs
return nil
}
type AttrAddActionTrigger struct {
ActionTriggersId string
Tenant string
Account string
ThresholdType string
ThresholdValue float64
BalanceId string
BalanceType string
BalanceDestinationIds string
BalanceRatingSubject string
BalanceWeight float64
BalanceExpiryTime string
BalanceSharedGroup string
Weight float64
ActionsId string
}
// Deprecated in rc8, replaced by AddAccountActionTriggers
func (apierSv1 *APIerSv1) AddTriggeredAction(ctx *context.Context, attr AttrAddActionTrigger, reply *string) error {
if missing := utils.MissingStructFields(&attr, []string{utils.AccountField}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
tnt := attr.Tenant
if tnt == utils.EmptyString {
tnt = apierSv1.Config.GeneralCfg().DefaultTenant
}
at := &engine.ActionTrigger{
ID: attr.ActionTriggersId,
ThresholdType: attr.ThresholdType,
ThresholdValue: attr.ThresholdValue,
Balance: new(engine.BalanceFilter),
Weight: attr.Weight,
ActionsID: attr.ActionsId,
}
if attr.BalanceId != "" {
at.Balance.ID = utils.StringPointer(attr.BalanceId)
}
if attr.BalanceType != "" {
at.Balance.Type = utils.StringPointer(attr.BalanceType)
}
if attr.BalanceDestinationIds != "" {
dstIDsMp := utils.StringMapFromSlice(strings.Split(attr.BalanceDestinationIds, utils.InfieldSep))
at.Balance.DestinationIDs = &dstIDsMp
}
if attr.BalanceRatingSubject != "" {
at.Balance.RatingSubject = utils.StringPointer(attr.BalanceRatingSubject)
}
if attr.BalanceWeight != 0.0 {
at.Balance.Weight = utils.Float64Pointer(attr.BalanceWeight)
}
if balExpiryTime, err := utils.ParseTimeDetectLayout(attr.BalanceExpiryTime,
apierSv1.Config.GeneralCfg().DefaultTimezone); err != nil {
return utils.NewErrServerError(err)
} else {
at.Balance.ExpirationDate = &balExpiryTime
}
if attr.BalanceSharedGroup != "" {
at.Balance.SharedGroups = &utils.StringMap{attr.BalanceSharedGroup: true}
}
acntID := utils.ConcatenatedKey(tnt, attr.Account)
err := guardian.Guardian.Guard(func() error {
acnt, err := apierSv1.DataManager.GetAccount(acntID)
if err != nil {
return err
}
acnt.ActionTriggers = append(acnt.ActionTriggers, at)
return apierSv1.DataManager.SetAccount(acnt)
}, config.CgrConfig().GeneralCfg().LockingTimeout, utils.AccountPrefix+acntID)
if err != nil {
return err
}
*reply = utils.OK
return nil
}