mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
Add models for Chargers and test for them
This commit is contained in:
committed by
Dan Christian Bogos
parent
463c974291
commit
43146a9c26
@@ -116,3 +116,7 @@ func (cS *ChargerService) V1ProcessEvent(args *utils.CGREvent,
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func (cpp *ChargerProfile) TenantID() string {
|
||||
return utils.ConcatenatedKey(cpp.Tenant, cpp.ID)
|
||||
}
|
||||
|
||||
@@ -2112,7 +2112,6 @@ func APItoModelStats(st *utils.TPStats) (mdls TpStatsS) {
|
||||
for _, val := range st.Metrics {
|
||||
if val.Parameters != "" {
|
||||
paramSlice = append(paramSlice, val.Parameters)
|
||||
|
||||
}
|
||||
}
|
||||
for i, val := range utils.StringMapFromSlice(paramSlice).Slice() {
|
||||
@@ -2836,3 +2835,108 @@ func APItoAttributeProfile(tpTH *utils.TPAttributeProfile, timezone string) (th
|
||||
}
|
||||
return th, nil
|
||||
}
|
||||
|
||||
type TPChargers []*TPCharger
|
||||
|
||||
func (tps TPChargers) AsTPChargers() (result []*utils.TPChargerProfile) {
|
||||
mst := make(map[string]*utils.TPChargerProfile)
|
||||
for _, tp := range tps {
|
||||
tpCPP, found := mst[tp.ID]
|
||||
if !found {
|
||||
tpCPP = &utils.TPChargerProfile{
|
||||
TPid: tp.Tpid,
|
||||
Tenant: tp.Tenant,
|
||||
ID: tp.ID,
|
||||
}
|
||||
}
|
||||
if tp.Weight != 0 {
|
||||
tpCPP.Weight = tp.Weight
|
||||
}
|
||||
if len(tp.ActivationInterval) != 0 {
|
||||
tpCPP.ActivationInterval = new(utils.TPActivationInterval)
|
||||
aiSplt := strings.Split(tp.ActivationInterval, utils.INFIELD_SEP)
|
||||
if len(aiSplt) == 2 {
|
||||
tpCPP.ActivationInterval.ActivationTime = aiSplt[0]
|
||||
tpCPP.ActivationInterval.ExpiryTime = aiSplt[1]
|
||||
} else if len(aiSplt) == 1 {
|
||||
tpCPP.ActivationInterval.ActivationTime = aiSplt[0]
|
||||
}
|
||||
}
|
||||
if tp.FilterIDs != "" {
|
||||
filterSplit := strings.Split(tp.FilterIDs, utils.INFIELD_SEP)
|
||||
tpCPP.FilterIDs = append(tpCPP.FilterIDs, filterSplit...)
|
||||
}
|
||||
if tp.RunID != "" {
|
||||
tpCPP.RunID = tp.RunID
|
||||
}
|
||||
if tp.AttributeIDs != "" {
|
||||
attributeSplit := strings.Split(tp.AttributeIDs, utils.INFIELD_SEP)
|
||||
tpCPP.AttributeIDs = append(tpCPP.AttributeIDs, attributeSplit...)
|
||||
}
|
||||
mst[tp.ID] = tpCPP
|
||||
}
|
||||
result = make([]*utils.TPChargerProfile, len(mst))
|
||||
i := 0
|
||||
for _, tp := range mst {
|
||||
result[i] = tp
|
||||
i++
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func APItoModelTPCharger(tpCPP *utils.TPChargerProfile) (mdls TPChargers) {
|
||||
if tpCPP != nil {
|
||||
mdl := &TPCharger{
|
||||
Tenant: tpCPP.Tenant,
|
||||
Tpid: tpCPP.TPid,
|
||||
ID: tpCPP.ID,
|
||||
}
|
||||
mdl.Weight = tpCPP.Weight
|
||||
mdl.RunID = tpCPP.RunID
|
||||
for i, val := range tpCPP.AttributeIDs {
|
||||
if i != 0 {
|
||||
mdl.AttributeIDs += utils.INFIELD_SEP
|
||||
}
|
||||
mdl.AttributeIDs += val
|
||||
}
|
||||
for i, val := range tpCPP.FilterIDs {
|
||||
if i != 0 {
|
||||
mdl.FilterIDs += utils.INFIELD_SEP
|
||||
}
|
||||
mdl.FilterIDs += val
|
||||
}
|
||||
if tpCPP.ActivationInterval != nil {
|
||||
if tpCPP.ActivationInterval.ActivationTime != "" {
|
||||
mdl.ActivationInterval = tpCPP.ActivationInterval.ActivationTime
|
||||
}
|
||||
if tpCPP.ActivationInterval.ExpiryTime != "" {
|
||||
mdl.ActivationInterval += utils.INFIELD_SEP + tpCPP.ActivationInterval.ExpiryTime
|
||||
}
|
||||
}
|
||||
mdls = append(mdls, mdl)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func APItoChargerProfile(tpCPP *utils.TPChargerProfile, timezone string) (cpp *ChargerProfile, err error) {
|
||||
cpp = &ChargerProfile{
|
||||
Tenant: tpCPP.Tenant,
|
||||
ID: tpCPP.ID,
|
||||
Weight: tpCPP.Weight,
|
||||
RunID: tpCPP.RunID,
|
||||
FilterIDs: []string{},
|
||||
AttributeIDs: []string{},
|
||||
}
|
||||
for _, fli := range tpCPP.FilterIDs {
|
||||
cpp.FilterIDs = append(cpp.FilterIDs, fli)
|
||||
}
|
||||
for _, attribute := range tpCPP.AttributeIDs {
|
||||
cpp.AttributeIDs = append(cpp.AttributeIDs, attribute)
|
||||
}
|
||||
if tpCPP.ActivationInterval != nil {
|
||||
if cpp.ActivationInterval, err = tpCPP.ActivationInterval.AsActivationInterval(timezone); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return cpp, nil
|
||||
}
|
||||
|
||||
@@ -1329,3 +1329,100 @@ func TestModelAsTPAttribute(t *testing.T) {
|
||||
t.Errorf("Expecting : %+v, received: %+v", utils.ToJSON(expected), utils.ToJSON(rcv[0]))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPItoChargerProfile(t *testing.T) {
|
||||
tpCPP := &utils.TPChargerProfile{
|
||||
TPid: "TP1",
|
||||
Tenant: "cgrates.org",
|
||||
ID: "Charger1",
|
||||
FilterIDs: []string{"FLTR_ACNT_dan", "FLTR_DST_DE"},
|
||||
RunID: "*rated",
|
||||
ActivationInterval: &utils.TPActivationInterval{
|
||||
ActivationTime: "2014-07-14T14:35:00Z",
|
||||
ExpiryTime: "",
|
||||
},
|
||||
AttributeIDs: []string{"ATTR1", "ATTR2"},
|
||||
Weight: 20,
|
||||
}
|
||||
|
||||
expected := &ChargerProfile{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "Charger1",
|
||||
FilterIDs: []string{"FLTR_ACNT_dan", "FLTR_DST_DE"},
|
||||
ActivationInterval: &utils.ActivationInterval{
|
||||
ActivationTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC),
|
||||
},
|
||||
RunID: "*rated",
|
||||
AttributeIDs: []string{"ATTR1", "ATTR2"},
|
||||
Weight: 20,
|
||||
}
|
||||
if rcv, err := APItoChargerProfile(tpCPP, "UTC"); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(expected, rcv) {
|
||||
t.Errorf("Expecting : %+v, received: %+v", utils.ToJSON(expected), utils.ToJSON(rcv))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPItoModelTPCharger(t *testing.T) {
|
||||
tpCharger := &utils.TPChargerProfile{
|
||||
TPid: "TP1",
|
||||
Tenant: "cgrates.org",
|
||||
ID: "Charger1",
|
||||
FilterIDs: []string{"FLTR_ACNT_dan", "FLTR_DST_DE"},
|
||||
RunID: "*rated",
|
||||
ActivationInterval: &utils.TPActivationInterval{
|
||||
ActivationTime: "2014-07-14T14:35:00Z",
|
||||
ExpiryTime: "",
|
||||
},
|
||||
AttributeIDs: []string{"ATTR1", "ATTR2"},
|
||||
Weight: 20,
|
||||
}
|
||||
expected := TPChargers{
|
||||
&TPCharger{
|
||||
Tpid: "TP1",
|
||||
Tenant: "cgrates.org",
|
||||
ID: "Charger1",
|
||||
FilterIDs: "FLTR_ACNT_dan;FLTR_DST_DE",
|
||||
RunID: "*rated",
|
||||
AttributeIDs: "ATTR1;ATTR2",
|
||||
ActivationInterval: "2014-07-14T14:35:00Z",
|
||||
Weight: 20,
|
||||
},
|
||||
}
|
||||
rcv := APItoModelTPCharger(tpCharger)
|
||||
if !reflect.DeepEqual(expected, rcv) {
|
||||
t.Errorf("Expecting : %+v, received: %+v", utils.ToJSON(expected), utils.ToJSON(rcv))
|
||||
}
|
||||
}
|
||||
|
||||
func TestModelAsTPChargers(t *testing.T) {
|
||||
models := TPChargers{
|
||||
&TPCharger{
|
||||
Tpid: "TP1",
|
||||
Tenant: "cgrates.org",
|
||||
ID: "Charger1",
|
||||
FilterIDs: "FLTR_ACNT_dan;FLTR_DST_DE",
|
||||
RunID: "*rated",
|
||||
AttributeIDs: "ATTR1;ATTR2",
|
||||
ActivationInterval: "2014-07-14T14:35:00Z",
|
||||
Weight: 20,
|
||||
},
|
||||
}
|
||||
expected := &utils.TPChargerProfile{
|
||||
TPid: "TP1",
|
||||
Tenant: "cgrates.org",
|
||||
ID: "Charger1",
|
||||
FilterIDs: []string{"FLTR_ACNT_dan", "FLTR_DST_DE"},
|
||||
RunID: "*rated",
|
||||
ActivationInterval: &utils.TPActivationInterval{
|
||||
ActivationTime: "2014-07-14T14:35:00Z",
|
||||
ExpiryTime: "",
|
||||
},
|
||||
AttributeIDs: []string{"ATTR1", "ATTR2"},
|
||||
Weight: 20,
|
||||
}
|
||||
rcv := models.AsTPChargers()
|
||||
if !reflect.DeepEqual(expected, rcv[0]) {
|
||||
t.Errorf("Expecting : %+v, received: %+v", utils.ToJSON(expected), utils.ToJSON(rcv[0]))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -759,7 +759,7 @@ func (csvs *CSVStorage) GetTPAttributes(tpid, id string) ([]*utils.TPAttributePr
|
||||
return nil, err
|
||||
}
|
||||
if attributeProfile, err := csvLoad(TPAttribute{}, record); err != nil {
|
||||
log.Print("error loading tpAliasProfile: ", err)
|
||||
log.Print("error loading tpAttributeProfile: ", err)
|
||||
return nil, err
|
||||
} else {
|
||||
attributeProfile := attributeProfile.(TPAttribute)
|
||||
@@ -771,31 +771,31 @@ func (csvs *CSVStorage) GetTPAttributes(tpid, id string) ([]*utils.TPAttributePr
|
||||
}
|
||||
|
||||
func (csvs *CSVStorage) GetTPChargers(tpid, id string) ([]*utils.TPChargerProfile, error) {
|
||||
// csvReader, fp, err := csvs.readerFunc(csvs.chargerProfilesFn, csvs.sep, getColumnCount(TPCharger{}))
|
||||
// if err != nil {
|
||||
// //log.Print("Could not load AttributeProfile file: ", err)
|
||||
// // allow writing of the other values
|
||||
// return nil, nil
|
||||
// }
|
||||
// if fp != nil {
|
||||
// defer fp.Close()
|
||||
// }
|
||||
// var tpAls TPAttributes
|
||||
// for record, err := csvReader.Read(); err != io.EOF; record, err = csvReader.Read() {
|
||||
// if err != nil {
|
||||
// log.Printf("bad line in %s, %s\n", csvs.chargerProfilesFn, err.Error())
|
||||
// return nil, err
|
||||
// }
|
||||
// if attributeProfile, err := csvLoad(TPAttribute{}, record); err != nil {
|
||||
// log.Print("error loading tpAliasProfile: ", err)
|
||||
// return nil, err
|
||||
// } else {
|
||||
// attributeProfile := attributeProfile.(TPAttribute)
|
||||
// attributeProfile.Tpid = tpid
|
||||
// tpAls = append(tpAls, &attributeProfile)
|
||||
// }
|
||||
// }
|
||||
return nil, nil
|
||||
csvReader, fp, err := csvs.readerFunc(csvs.chargerProfilesFn, csvs.sep, getColumnCount(TPCharger{}))
|
||||
if err != nil {
|
||||
//log.Print("Could not load AttributeProfile file: ", err)
|
||||
// allow writing of the other values
|
||||
return nil, nil
|
||||
}
|
||||
if fp != nil {
|
||||
defer fp.Close()
|
||||
}
|
||||
var tpCPPs TPChargers
|
||||
for record, err := csvReader.Read(); err != io.EOF; record, err = csvReader.Read() {
|
||||
if err != nil {
|
||||
log.Printf("bad line in %s, %s\n", csvs.chargerProfilesFn, err.Error())
|
||||
return nil, err
|
||||
}
|
||||
if cpp, err := csvLoad(TPCharger{}, record); err != nil {
|
||||
log.Print("error loading tpChargerProfile: ", err)
|
||||
return nil, err
|
||||
} else {
|
||||
cpp := cpp.(TPCharger)
|
||||
cpp.Tpid = tpid
|
||||
tpCPPs = append(tpCPPs, &cpp)
|
||||
}
|
||||
}
|
||||
return tpCPPs.AsTPChargers(), nil
|
||||
}
|
||||
|
||||
func (csvs *CSVStorage) GetTpIds(colName string) ([]string, error) {
|
||||
|
||||
@@ -738,6 +738,28 @@ func (self *SQLStorage) SetTPAttributes(tpAttrs []*utils.TPAttributeProfile) err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SQLStorage) SetTPChargers(tpCPPs []*utils.TPChargerProfile) error {
|
||||
if len(tpCPPs) == 0 {
|
||||
return nil
|
||||
}
|
||||
tx := self.db.Begin()
|
||||
for _, cpp := range tpCPPs {
|
||||
// Remove previous
|
||||
if err := tx.Where(&TPCharger{Tpid: cpp.TPid, ID: cpp.ID}).Delete(TPCharger{}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
for _, mst := range APItoModelTPCharger(cpp) {
|
||||
if err := tx.Save(&mst).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
tx.Commit()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SQLStorage) SetSMCost(smc *SMCost) error {
|
||||
if smc.CostDetails == nil {
|
||||
return nil
|
||||
@@ -1624,7 +1646,19 @@ func (self *SQLStorage) GetTPAttributes(tpid, id string) ([]*utils.TPAttributePr
|
||||
}
|
||||
|
||||
func (self *SQLStorage) GetTPChargers(tpid, id string) ([]*utils.TPChargerProfile, error) {
|
||||
return nil, nil
|
||||
var cpps TPChargers
|
||||
q := self.db.Where("tpid = ?", tpid)
|
||||
if len(id) != 0 {
|
||||
q = q.Where("id = ?", id)
|
||||
}
|
||||
if err := q.Find(&cpps).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
arls := cpps.AsTPChargers()
|
||||
if len(arls) == 0 {
|
||||
return arls, utils.ErrNotFound
|
||||
}
|
||||
return arls, nil
|
||||
}
|
||||
|
||||
// GetVersions returns slice of all versions or a specific version if tag is specified
|
||||
|
||||
@@ -58,11 +58,13 @@ type TpReader struct {
|
||||
filters map[utils.TenantID]*utils.TPFilterProfile
|
||||
sppProfiles map[utils.TenantID]*utils.TPSupplierProfile
|
||||
attributeProfiles map[utils.TenantID]*utils.TPAttributeProfile
|
||||
chargerProfiles map[utils.TenantID]*utils.TPChargerProfile
|
||||
resources []*utils.TenantID // IDs of resources which need creation based on resourceProfiles
|
||||
statQueues []*utils.TenantID // IDs of statQueues which need creation based on statQueueProfiles
|
||||
thresholds []*utils.TenantID // IDs of thresholds which need creation based on thresholdProfiles
|
||||
suppliers []*utils.TenantID // IDs of suppliers which need creation based on sppProfiles
|
||||
attrTntID []*utils.TenantID // IDs of suppliers which need creation based on attributeProfiles
|
||||
chargers []*utils.TenantID // IDs of chargers which need creation based on chargerProfiles
|
||||
revDests,
|
||||
revAliases,
|
||||
acntActionPlans map[string][]string
|
||||
@@ -138,6 +140,7 @@ func (tpr *TpReader) Init() {
|
||||
tpr.thProfiles = make(map[utils.TenantID]*utils.TPThreshold)
|
||||
tpr.sppProfiles = make(map[utils.TenantID]*utils.TPSupplierProfile)
|
||||
tpr.attributeProfiles = make(map[utils.TenantID]*utils.TPAttributeProfile)
|
||||
tpr.chargerProfiles = make(map[utils.TenantID]*utils.TPChargerProfile)
|
||||
tpr.filters = make(map[utils.TenantID]*utils.TPFilterProfile)
|
||||
tpr.revDests = make(map[string][]string)
|
||||
tpr.revAliases = make(map[string][]string)
|
||||
@@ -1738,6 +1741,30 @@ func (tpr *TpReader) LoadAttributeProfiles() error {
|
||||
return tpr.LoadAttributeProfilesFiltered("")
|
||||
}
|
||||
|
||||
func (tpr *TpReader) LoadChargerProfilesFiltered(tag string) (err error) {
|
||||
rls, err := tpr.lr.GetTPChargers(tpr.tpid, tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mapChargerProfile := make(map[utils.TenantID]*utils.TPChargerProfile)
|
||||
for _, rl := range rls {
|
||||
mapChargerProfile[utils.TenantID{Tenant: rl.Tenant, ID: rl.ID}] = rl
|
||||
}
|
||||
tpr.chargerProfiles = mapChargerProfile
|
||||
for tntID, _ := range mapChargerProfile {
|
||||
if has, err := tpr.dm.HasData(utils.ChargerProfilePrefix, tntID.ID, tntID.Tenant); err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
tpr.chargers = append(tpr.chargers, &utils.TenantID{Tenant: tntID.Tenant, ID: tntID.ID})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tpr *TpReader) LoadChargerProfiles() error {
|
||||
return tpr.LoadChargerProfilesFiltered("")
|
||||
}
|
||||
|
||||
func (tpr *TpReader) LoadAll() (err error) {
|
||||
if err = tpr.LoadDestinations(); err != nil && err.Error() != utils.NotFoundCaps {
|
||||
return
|
||||
@@ -1805,6 +1832,9 @@ func (tpr *TpReader) LoadAll() (err error) {
|
||||
if err = tpr.LoadAttributeProfiles(); err != nil && err.Error() != utils.NotFoundCaps {
|
||||
return
|
||||
}
|
||||
if err = tpr.LoadChargerProfiles(); err != nil && err.Error() != utils.NotFoundCaps {
|
||||
return
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2175,6 +2205,23 @@ func (tpr *TpReader) WriteToDatabase(flush, verbose, disable_reverse bool) (err
|
||||
}
|
||||
}
|
||||
|
||||
if verbose {
|
||||
log.Print("ChargerProfiles:")
|
||||
}
|
||||
for _, tpTH := range tpr.chargerProfiles {
|
||||
|
||||
th, err := APItoChargerProfile(tpTH, tpr.timezone)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = tpr.dm.SetChargerProfile(th, true); err != nil {
|
||||
return err
|
||||
}
|
||||
if verbose {
|
||||
log.Print("\t", th.TenantID())
|
||||
}
|
||||
}
|
||||
|
||||
if verbose {
|
||||
log.Print("Timings:")
|
||||
}
|
||||
@@ -2284,6 +2331,8 @@ func (tpr *TpReader) ShowStatistics() {
|
||||
log.Print("SupplierProfiles: ", len(tpr.sppProfiles))
|
||||
// Attribute profiles
|
||||
log.Print("AttributeProfiles: ", len(tpr.attributeProfiles))
|
||||
// Charger profiles
|
||||
log.Print("ChargerProfiles: ", len(tpr.chargerProfiles))
|
||||
}
|
||||
|
||||
// Returns the identities loaded for a specific category, useful for cache reloads
|
||||
@@ -2457,6 +2506,14 @@ func (tpr *TpReader) GetLoadedIds(categ string) ([]string, error) {
|
||||
i++
|
||||
}
|
||||
return keys, nil
|
||||
case utils.ChargerProfilePrefix:
|
||||
keys := make([]string, len(tpr.chargerProfiles))
|
||||
i := 0
|
||||
for k, _ := range tpr.chargerProfiles {
|
||||
keys[i] = k.TenantID()
|
||||
i++
|
||||
}
|
||||
return keys, nil
|
||||
}
|
||||
return nil, errors.New("Unsupported load category")
|
||||
}
|
||||
@@ -2722,6 +2779,31 @@ func (tpr *TpReader) RemoveFromDatabase(verbose, disable_reverse bool) (err erro
|
||||
log.Print("\t", tpTH.Tenant)
|
||||
}
|
||||
}
|
||||
|
||||
if verbose {
|
||||
log.Print("AttributeProfiles:")
|
||||
}
|
||||
for _, tpTH := range tpr.attributeProfiles {
|
||||
if err = tpr.dm.RemoveAttributeProfile(tpTH.Tenant, tpTH.ID, tpTH.Contexts, utils.NonTransactional, false); err != nil {
|
||||
return err
|
||||
}
|
||||
if verbose {
|
||||
log.Print("\t", tpTH.Tenant)
|
||||
}
|
||||
}
|
||||
|
||||
if verbose {
|
||||
log.Print("ChargerProfiles:")
|
||||
}
|
||||
for _, tpTH := range tpr.chargerProfiles {
|
||||
if err = tpr.dm.RemoveChargerProfile(tpTH.Tenant, tpTH.ID, utils.NonTransactional, false); err != nil {
|
||||
return err
|
||||
}
|
||||
if verbose {
|
||||
log.Print("\t", tpTH.Tenant)
|
||||
}
|
||||
}
|
||||
|
||||
if verbose {
|
||||
log.Print("Timings:")
|
||||
}
|
||||
|
||||
@@ -295,6 +295,17 @@ func (self *TPExporter) Run() error {
|
||||
}
|
||||
}
|
||||
|
||||
storDataChargers, err := self.storDb.GetTPChargers(self.tpID, "")
|
||||
if err != nil && err.Error() != utils.ErrNotFound.Error() {
|
||||
return err
|
||||
}
|
||||
for _, sd := range storDataChargers {
|
||||
sdModels := APItoModelTPCharger(sd)
|
||||
for _, sdModel := range sdModels {
|
||||
toExportMap[utils.ChargersCsv] = append(toExportMap[utils.ChargersCsv], sdModel)
|
||||
}
|
||||
}
|
||||
|
||||
storDataUsers, err := self.storDb.GetTPUsers(&utils.TPUsers{TPid: self.tpID})
|
||||
if err != nil && err.Error() != utils.ErrNotFound.Error() {
|
||||
return err
|
||||
|
||||
Reference in New Issue
Block a user