Allow multiple suppliers with the same ID

This commit is contained in:
TeoV
2020-05-08 17:34:15 +03:00
committed by Dan Christian Bogos
parent 109d3c2ea5
commit 2cc081592a
7 changed files with 256 additions and 20 deletions

View File

@@ -67,10 +67,11 @@ var (
testV1SplSRemSupplierProfiles,
testV1SplSGetSupplierForEvent,
// reset the database and load the TP again
// testV1SplSInitDataDb,
// testV1SplSFromFolder,
testV1SplSInitDataDb,
testV1SplSFromFolder,
// for the moment we decide to comment the tests
// testV1SplsOneSupplierWithoutDestination,
testV1SplMultipleSupplierSameID,
testV1SplSupplierPing,
testV1SplSStopEngine,
}
@@ -1119,6 +1120,120 @@ func testV1SplsOneSupplierWithoutDestination(t *testing.T) {
}
}
func testV1SplMultipleSupplierSameID(t *testing.T) {
var reply *engine.SupplierProfile
if err := splSv1Rpc.Call(utils.APIerSv1GetSupplierProfile,
&utils.TenantID{Tenant: "cgrates.org", ID: "MULTIPLE_ROUTES"}, &reply); err == nil ||
err.Error() != utils.ErrNotFound.Error() {
t.Error(err)
}
splPrf = &SupplierWithCache{
SupplierProfile: &engine.SupplierProfile{
Tenant: "cgrates.org",
ID: "MULTIPLE_ROUTES",
FilterIDs: []string{"*string:~*req.Account:SpecialCase2"},
Sorting: utils.MetaLC,
Suppliers: []*engine.Supplier{
{
ID: "Route1",
RatingPlanIDs: []string{"RP_LOCAL"},
FilterIDs: []string{"*string:~*req.Month:April"},
Weight: 10,
},
{
ID: "Route1",
RatingPlanIDs: []string{"RP_MOBILE"},
FilterIDs: []string{"*string:~*req.Month:May"},
Weight: 10,
},
},
Weight: 100,
},
}
var result string
if err := splSv1Rpc.Call(utils.APIerSv1SetSupplierProfile, splPrf, &result); err != nil {
t.Error(err)
} else if result != utils.OK {
t.Error("Unexpected reply returned", result)
}
tNow := time.Now()
ev := &engine.ArgsGetSuppliers{
CGREvent: &utils.CGREvent{
Tenant: "cgrates.org",
Time: &tNow,
ID: "testV1RouteMultipleRouteSameID",
Event: map[string]interface{}{
utils.Account: "SpecialCase2",
utils.Destination: "+135876",
utils.SetupTime: utils.MetaNow,
utils.Usage: "2m",
"Month": "April",
},
},
}
eSpls := engine.SortedSuppliers{
ProfileID: "MULTIPLE_ROUTES",
Sorting: utils.MetaLC,
Count: 1,
SortedSuppliers: []*engine.SortedSupplier{
{
SupplierID: "Route1",
SortingData: map[string]interface{}{
utils.Cost: 0.0396,
"RatingPlanID": "RP_LOCAL",
utils.Weight: 10.0,
},
},
},
}
var suplsReply engine.SortedSuppliers
if err := splSv1Rpc.Call(utils.SupplierSv1GetSuppliers,
ev, &suplsReply); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eSpls, suplsReply) {
t.Errorf("Expecting: %s, received: %s",
utils.ToJSON(eSpls), utils.ToJSON(suplsReply))
}
ev = &engine.ArgsGetSuppliers{
CGREvent: &utils.CGREvent{
Tenant: "cgrates.org",
Time: &tNow,
ID: "testV1RouteMultipleRouteSameID",
Event: map[string]interface{}{
utils.Account: "SpecialCase2",
utils.Destination: "+135876",
utils.SetupTime: utils.MetaNow,
utils.Usage: "2m",
"Month": "May",
},
},
}
eSpls = engine.SortedSuppliers{
ProfileID: "MULTIPLE_ROUTES",
Sorting: utils.MetaLC,
Count: 1,
SortedSuppliers: []*engine.SortedSupplier{
{
SupplierID: "Route1",
SortingData: map[string]interface{}{
utils.Cost: 0.0204,
"RatingPlanID": "RP_MOBILE",
utils.Weight: 10.0,
},
},
},
}
if err := splSv1Rpc.Call(utils.SupplierSv1GetSuppliers,
ev, &suplsReply); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eSpls, suplsReply) {
t.Errorf("Expecting: %s, received: %s",
utils.ToJSON(eSpls), utils.ToJSON(suplsReply))
}
}
func testV1SplSStopEngine(t *testing.T) {
if err := engine.KillEngine(100); err != nil {
t.Error(err)

View File

@@ -1239,24 +1239,57 @@ func TestLoadSupplierProfiles(t *testing.T) {
Suppliers: []*utils.TPSupplier{
&utils.TPSupplier{
ID: "supplier1",
FilterIDs: []string{"FLTR_ACNT_dan", "FLTR_DST_DE"},
AccountIDs: []string{"Account1", "Account1_1", "Account2"},
RatingPlanIDs: []string{"RPL_1", "RPL_2", "RPL_3"},
ResourceIDs: []string{"ResGroup1", "ResGroup2", "ResGroup3", "ResGroup4"},
StatIDs: []string{"Stat1", "Stat2", "Stat3"},
FilterIDs: []string{"FLTR_DST_DE"},
AccountIDs: []string{"Account2"},
RatingPlanIDs: []string{"RPL_3"},
ResourceIDs: []string{"ResGroup3"},
StatIDs: []string{"Stat2"},
Weight: 10,
Blocker: false,
SupplierParameters: utils.EmptyString,
},
&utils.TPSupplier{
ID: "supplier1",
FilterIDs: []string{"FLTR_ACNT_dan"},
AccountIDs: []string{"Account1", "Account1_1"},
RatingPlanIDs: []string{"RPL_1"},
ResourceIDs: []string{"ResGroup1"},
StatIDs: []string{"Stat1"},
Weight: 10,
Blocker: true,
SupplierParameters: "param1",
},
&utils.TPSupplier{
ID: "supplier1",
RatingPlanIDs: []string{"RPL_2"},
ResourceIDs: []string{"ResGroup2", "ResGroup4"},
StatIDs: []string{"Stat3"},
Weight: 10,
Blocker: false,
SupplierParameters: utils.EmptyString,
},
},
Weight: 20,
},
}
resKey := utils.TenantID{Tenant: "cgrates.org", ID: "SPP_1"}
sort.Slice(eSppProfiles[resKey].Suppliers, func(i, j int) bool {
return strings.Compare(eSppProfiles[resKey].Suppliers[i].ID+
strings.Join(eSppProfiles[resKey].Suppliers[i].FilterIDs, utils.CONCATENATED_KEY_SEP),
eSppProfiles[resKey].Suppliers[j].ID+strings.Join(eSppProfiles[resKey].Suppliers[j].FilterIDs, utils.CONCATENATED_KEY_SEP)) < 0
})
if len(csvr.sppProfiles) != len(eSppProfiles) {
t.Errorf("Failed to load SupplierProfiles: %s", utils.ToIJSON(csvr.sppProfiles))
} else if !reflect.DeepEqual(eSppProfiles[resKey], csvr.sppProfiles[resKey]) {
t.Errorf("Expecting: %+v, received: %+v", eSppProfiles[resKey], csvr.sppProfiles[resKey])
} else {
sort.Slice(csvr.sppProfiles[resKey].Suppliers, func(i, j int) bool {
return strings.Compare(csvr.sppProfiles[resKey].Suppliers[i].ID+
strings.Join(csvr.sppProfiles[resKey].Suppliers[i].FilterIDs, utils.CONCATENATED_KEY_SEP),
csvr.sppProfiles[resKey].Suppliers[j].ID+strings.Join(csvr.sppProfiles[resKey].Suppliers[j].FilterIDs, utils.CONCATENATED_KEY_SEP)) < 0
})
if !reflect.DeepEqual(eSppProfiles[resKey], csvr.sppProfiles[resKey]) {
t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(eSppProfiles[resKey]), utils.ToJSON(csvr.sppProfiles[resKey]))
}
}
}

View File

@@ -1809,7 +1809,12 @@ func (tps TpSuppliers) AsTPSuppliers() (result []*utils.TPSupplierProfile) {
if _, has := suppliersMap[tenID]; !has {
suppliersMap[(&utils.TenantID{Tenant: tp.Tenant, ID: tp.ID}).TenantID()] = make(map[string]*utils.TPSupplier)
}
sup, found := suppliersMap[tenID][tp.SupplierID]
supID := tp.SupplierID
if tp.SupplierFilterIDs != "" {
supID = utils.ConcatenatedKey(supID,
utils.NewStringSet(strings.Split(tp.SupplierFilterIDs, utils.INFIELD_SEP)).Sha1())
}
sup, found := suppliersMap[tenID][supID]
if !found {
sup = &utils.TPSupplier{
ID: tp.SupplierID,
@@ -1840,7 +1845,7 @@ func (tps TpSuppliers) AsTPSuppliers() (result []*utils.TPSupplierProfile) {
accSplit := strings.Split(tp.SupplierAccountIDs, utils.INFIELD_SEP)
sup.AccountIDs = append(sup.AccountIDs, accSplit...)
}
suppliersMap[(&utils.TenantID{Tenant: tp.Tenant, ID: tp.ID}).TenantID()][tp.SupplierID] = sup
suppliersMap[(&utils.TenantID{Tenant: tp.Tenant, ID: tp.ID}).TenantID()][supID] = sup
}
if tp.SortingParameters != "" {
if _, has := sortingParameterMap[tenID]; !has {

View File

@@ -770,25 +770,56 @@ func TestLoaderProcessSuppliers(t *testing.T) {
Suppliers: []*engine.Supplier{
&engine.Supplier{
ID: "supplier1",
FilterIDs: []string{"FLTR_ACNT_dan", "FLTR_DST_DE"},
AccountIDs: []string{"Account1", "Account1_1", "Account2"},
RatingPlanIDs: []string{"RPL_1", "RPL_2", "RPL_3"},
ResourceIDs: []string{"ResGroup1", "ResGroup2", "ResGroup3", "ResGroup4"},
StatIDs: []string{"Stat1", "Stat2", "Stat3"},
FilterIDs: []string{"FLTR_DST_DE"},
AccountIDs: []string{"Account2"},
RatingPlanIDs: []string{"RPL_3"},
ResourceIDs: []string{"ResGroup3"},
StatIDs: []string{"Stat2"},
Weight: 10,
Blocker: false,
SupplierParameters: utils.EmptyString,
},
&engine.Supplier{
ID: "supplier1",
FilterIDs: []string{"FLTR_ACNT_dan"},
AccountIDs: []string{"Account1", "Account1_1"},
RatingPlanIDs: []string{"RPL_1"},
ResourceIDs: []string{"ResGroup1"},
StatIDs: []string{"Stat1"},
Weight: 10,
Blocker: true,
SupplierParameters: "param1",
},
&engine.Supplier{
ID: "supplier1",
RatingPlanIDs: []string{"RPL_2"},
ResourceIDs: []string{"ResGroup2", "ResGroup4"},
StatIDs: []string{"Stat3"},
Weight: 10,
Blocker: false,
SupplierParameters: utils.EmptyString,
},
},
Weight: 20,
}
sort.Slice(eSp3.Suppliers, func(i, j int) bool {
return strings.Compare(eSp3.Suppliers[i].ID+
strings.Join(eSp3.Suppliers[i].FilterIDs, utils.CONCATENATED_KEY_SEP),
eSp3.Suppliers[j].ID+strings.Join(eSp3.Suppliers[j].FilterIDs, utils.CONCATENATED_KEY_SEP)) < 0
})
if aps, err := ldr.dm.GetSupplierProfile("cgrates.org", "SPP_1",
true, false, utils.NonTransactional); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eSp3, aps) {
t.Errorf("expecting: %s, received: %s",
utils.ToJSON(eSp3), utils.ToJSON(aps))
} else {
sort.Slice(aps.Suppliers, func(i, j int) bool {
return strings.Compare(aps.Suppliers[i].ID+
strings.Join(aps.Suppliers[i].FilterIDs, utils.CONCATENATED_KEY_SEP),
aps.Suppliers[j].ID+strings.Join(aps.Suppliers[j].FilterIDs, utils.CONCATENATED_KEY_SEP)) < 0
})
if !reflect.DeepEqual(eSp3, aps) {
t.Errorf("expecting: %s, received: %s",
utils.ToJSON(eSp3), utils.ToJSON(aps))
}
}
}

View File

@@ -23,6 +23,7 @@ cgrates (0.10.1~dev) UNRELEASED; urgency=medium
* [SessionS] Update subflags for *rals ( *authorize and *initiate )
* [AgentRequest] Improved NavigableMap
* [AgentRequest] FieldAsInterface return Data instead of NMItem
* [SupplierS] Allow multiple suppliers with the same ID
-- Alexandru Tripon <alexandru.tripon@itsyscom.com> Wed, 19 Feb 2020 15:22:59 +0200

View File

@@ -18,6 +18,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package utils
import "sort"
// NewStringSet returns a new StringSet
func NewStringSet(dataSlice []string) (s StringSet) {
s = make(StringSet)
@@ -62,6 +64,18 @@ func (s StringSet) AsSlice() []string {
return result
}
// AsOrderedSlice returns the keys as ordered string slice
func (s StringSet) AsOrderedSlice() (ss []string) {
ss = s.AsSlice()
sort.Strings(ss)
return
}
// Sha1 returns the Sha1 on top of ordered slice
func (s StringSet) Sha1() string {
return Sha1(s.AsOrderedSlice()...)
}
// Size returns the size of the set
func (s StringSet) Size() int {
return len(s)

View File

@@ -115,6 +115,43 @@ func TestAsSlice(t *testing.T) {
}
}
func TestAsOrderedSlice(t *testing.T) {
s := StringSet{}
eOut := make([]string, 0)
if rcv := s.AsOrderedSlice(); !reflect.DeepEqual(eOut, rcv) {
t.Errorf("Expecting: %+v, received: %+v", eOut, rcv)
}
s = StringSet{
"test3": struct{}{},
"test12": struct{}{},
"test2": struct{}{}}
eOut = []string{"test12", "test2", "test3"}
rcv := s.AsOrderedSlice()
if !reflect.DeepEqual(eOut, rcv) {
t.Errorf("Expecting: %+v, received: %+v", eOut, rcv)
}
}
func TestSetSha1(t *testing.T) {
s := StringSet{
"test3": struct{}{},
"test12": struct{}{},
"test2": struct{}{}}
eOut := "8fbb49ecf2ee4116bc492505865d2125a78f2161"
if rcv := s.Sha1(); rcv != eOut {
t.Errorf("Expecting: %+v, received: %+v", eOut, rcv)
}
s2 := StringSet{
"test2": struct{}{},
"test3": struct{}{},
"test12": struct{}{},
}
if rcv := s2.Sha1(); rcv != eOut {
t.Errorf("Expecting: %+v, received: %+v", eOut, rcv)
}
}
func TestSize(t *testing.T) {
s := StringSet{}
if rcv := s.Size(); rcv != 0 {