Adding CGREvent

This commit is contained in:
DanB
2017-12-01 15:41:17 +01:00
parent 920e29f053
commit f5d028aeba
4 changed files with 183 additions and 28 deletions

View File

@@ -19,7 +19,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package engine
import (
"errors"
"fmt"
"sort"
"time"
@@ -70,6 +69,15 @@ type SupplierEvent struct {
Event map[string]interface{}
}
func (se *SupplierEvent) CheckMandatoryFields(fldNames []string) error {
for _, fldName := range fldNames {
if _, has := se.Event[fldName]; !has {
return utils.NewErrMandatoryIeMissing(fldName)
}
}
return nil
}
// AnswerTime returns the AnswerTime of SupplierEvent
func (le *SupplierEvent) FieldAsString(fldName string) (val string, err error) {
iface, has := le.Event[fldName]
@@ -78,41 +86,47 @@ func (le *SupplierEvent) FieldAsString(fldName string) (val string, err error) {
}
val, canCast := utils.CastFieldIfToString(iface)
if !canCast {
return "", errors.New("cannot cast to string")
return "", fmt.Errorf("cannot cast %s to string", fldName)
}
return val, nil
}
// AnswerTime returns the AnswerTime of SupplierEvent
func (le *SupplierEvent) AnswerTime(timezone string) (at time.Time, err error) {
atIf, has := le.Event[utils.ANSWER_TIME]
// FieldAsTime returns the a field as Time instance
func (le *SupplierEvent) FieldAsTime(fldName string, timezone string) (t time.Time, err error) {
iface, has := le.Event[fldName]
if !has {
return at, utils.ErrNotFound
err = utils.ErrNotFound
return
}
if at, canCast := atIf.(time.Time); canCast {
return at, nil
var canCast bool
if t, canCast = iface.(time.Time); canCast {
return
}
atStr, canCast := atIf.(string)
s, canCast := iface.(string)
if !canCast {
return at, errors.New("cannot cast to string")
err = fmt.Errorf("cannot cast %s to string", fldName)
return
}
return utils.ParseTimeDetectLayout(atStr, timezone)
return utils.ParseTimeDetectLayout(s, timezone)
}
// AnswerTime returns the AnswerTime of SupplierEvent
func (le *SupplierEvent) Usage() (usage time.Duration, err error) {
iface, has := le.Event[utils.USAGE]
// FieldAsTime returns the a field as Time instance
func (le *SupplierEvent) FieldAsDuration(fldName string) (d time.Duration, err error) {
iface, has := le.Event[fldName]
if !has {
return 0, utils.ErrNotFound
err = utils.ErrNotFound
return
}
if usage, canCast := iface.(time.Duration); canCast {
return usage, nil
var canCast bool
if d, canCast = iface.(time.Duration); canCast {
return
}
usageStr, canCast := iface.(string)
s, canCast := iface.(string)
if !canCast {
return 0, errors.New("cannot cast to string")
err = fmt.Errorf("cannot cast %s to string", fldName)
return
}
return utils.ParseDurationWithNanosecs(usageStr)
return utils.ParseDurationWithNanosecs(s)
}
// SuppliersSorter is the interface which needs to be implemented by supplier sorters
@@ -158,7 +172,7 @@ func (ws *WeightSorter) SortSuppliers(prflID string,
for i, s := range suppls {
sortedSuppls.SortedSuppliers[i] = &SortedSupplier{
SupplierID: s.ID,
SortingData: map[string]interface{}{"Weight": s.Weight}}
SortingData: map[string]interface{}{Weight: s.Weight}}
}
sortedSuppls.SortWeight()
return

View File

@@ -34,14 +34,20 @@ type LeastCostSorter struct {
}
func (lcs *LeastCostSorter) SortSuppliers(prflID string,
suppls []*Supplier, suplEv *SupplierEvent) (sortedSuppls *SortedSuppliers, err error) {
suppls []*Supplier, ev *SupplierEvent) (sortedSuppls *SortedSuppliers, err error) {
sortedSuppls = &SortedSuppliers{ProfileID: prflID,
Sorting: lcs.sorting,
SortedSuppliers: make([]*SortedSupplier, len(suppls))}
for i, s := range suppls {
cost, err := lcs.spS.costForEvent(ev, s.AccountIDs, s.RatingPlanIDs)
if err != nil {
return nil, err
}
sortedSuppls.SortedSuppliers[i] = &SortedSupplier{
SupplierID: s.ID,
SortingData: map[string]interface{}{"Weight": s.Weight}}
SupplierID: s.ID,
SortingData: map[string]interface{}{
Weight: s.Weight,
Cost: cost}}
}
sortedSuppls.SortCost()
return

View File

@@ -23,6 +23,7 @@ import (
"sort"
"time"
//"github.com/cgrates/cgrates/cache"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/guardian"
"github.com/cgrates/cgrates/utils"
@@ -130,7 +131,7 @@ func (spS *SupplierService) matchingSupplierProfilesForEvent(ev *SupplierEvent)
}
return nil, err
}
aTime, err := ev.AnswerTime(spS.timezone)
aTime, err := ev.FieldAsTime(utils.ANSWER_TIME, spS.timezone)
if err != nil {
if err == utils.ErrNotFound {
aTime = time.Now()
@@ -142,7 +143,8 @@ func (spS *SupplierService) matchingSupplierProfilesForEvent(ev *SupplierEvent)
!lcrPrfl.ActivationInterval.IsActiveAtTime(aTime) { // not active
continue
}
if pass, err := spS.filterS.PassFiltersForEvent(ev.Tenant, ev.Event, lcrPrfl.FilterIDs); err != nil {
if pass, err := spS.filterS.PassFiltersForEvent(ev.Tenant,
ev.Event, lcrPrfl.FilterIDs); err != nil {
return nil, err
} else if !pass {
continue
@@ -166,8 +168,50 @@ func (spS *SupplierService) matchingSupplierProfilesForEvent(ev *SupplierEvent)
return
}
// costForEvent will compute cost out of ratingPlanIDs for event
func (spS *SupplierService) costForEvent(ev *SupplierEvent, rpIDs []string) (ec *EventCost, err error) {
// costForEvent will compute cost out of accounts and rating plans for event
func (spS *SupplierService) costForEvent(ev *SupplierEvent,
acntIDs, rpIDs []string) (ec *EventCost, err error) {
/*if err = ev.CheckMandatoryFields([]string{utils.ACCOUNT,
utils.DESTINATION, utils.ANSWER_TIME, utils.USAGE}); err != nil {
return
}
var acnt, subj, dst string
if acnt, err = ev.FieldAsString(utils.ACCOUNT); err != nil {
return
}
if subj, err = ev.FieldAsString(utils.ACCOUNT); err != nil {
if err != utils.ErrNotFound {
return
}
subj = acnt
}
if dst, err = ev.FieldAsString(utils.DESTINATION); err != nil {
return
}
var aTime time.Time
if aTime, err = ev.FieldAsTime(utils.ANSWER_TIME, spS.timezone); err != nil {
return
}
var usage time.Duration
if usage, err = ev.FieldAsDuration(utils.USAGE); err != nil {
return
}
for i, rp := range rpIDs {
rPrfl := &RatingProfile{
Id: utils.ConcatenatedKey(utils.OUT,
ev.Tenant, utils.MetaSuppliers, subj),
RatingPlanActivations: RatingPlanActivations{
&RatingPlanActivation{
ActivationTime: aTime,
RatingPlanId: rp,
},
},
}
// force cache set so it can be picked by calldescriptor for cost calculation
cache.Set(utils.RATING_PROFILE_PREFIX+rPrfl.Id, rPrfl,
true, utils.NonTransactional)
}
*/
return
}

91
utils/cgrevent.go Normal file
View File

@@ -0,0 +1,91 @@
/*
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 PURPOev. 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 <http://www.gnu.org/licenses/>
*/
package utils
import (
"fmt"
"time"
)
// CGREvent is a generic event processed by CGR services
type CGREvent struct {
Tenant string
ID string
Event map[string]interface{}
}
func (ev *CGREvent) CheckMandatoryFields(fldNames []string) error {
for _, fldName := range fldNames {
if _, has := ev.Event[fldName]; !has {
return NewErrMandatoryIeMissing(fldName)
}
}
return nil
}
// AnswerTime returns the AnswerTime of CGREvent
func (ev *CGREvent) FieldAsString(fldName string) (val string, err error) {
iface, has := ev.Event[fldName]
if !has {
return "", ErrNotFound
}
val, canCast := CastFieldIfToString(iface)
if !canCast {
return "", fmt.Errorf("cannot cast %s to string", fldName)
}
return val, nil
}
// FieldAsTime returns the a field as Time instance
func (ev *CGREvent) FieldAsTime(fldName string, timezone string) (t time.Time, err error) {
iface, has := ev.Event[fldName]
if !has {
err = ErrNotFound
return
}
var canCast bool
if t, canCast = iface.(time.Time); canCast {
return
}
s, canCast := iface.(string)
if !canCast {
err = fmt.Errorf("cannot cast %s to string", fldName)
return
}
return ParseTimeDetectLayout(s, timezone)
}
// FieldAsTime returns the a field as Time instance
func (ev *CGREvent) FieldAsDuration(fldName string) (d time.Duration, err error) {
iface, has := ev.Event[fldName]
if !has {
err = ErrNotFound
return
}
var canCast bool
if d, canCast = iface.(time.Duration); canCast {
return
}
s, canCast := iface.(string)
if !canCast {
err = fmt.Errorf("cannot cast %s to string", fldName)
return
}
return ParseDurationWithNanosecs(s)
}