From f2180fbc2fa414f044a2100e3bfabaf3a542e977 Mon Sep 17 00:00:00 2001 From: ionutboangiu Date: Wed, 30 Mar 2022 16:56:05 +0300 Subject: [PATCH] Implement pagination for rates --- apis/rates.go | 43 ++++++++++++++++++++++++++++++++----- config/config_defaults.go | 23 +++++++++++++++++++- config/ratescfg.go | 45 +++++++++++++++++++++++++++++++++++++++ utils/consts.go | 3 +++ 4 files changed, 108 insertions(+), 6 deletions(-) diff --git a/apis/rates.go b/apis/rates.go index dc741104d..33c00a1a5 100644 --- a/apis/rates.go +++ b/apis/rates.go @@ -19,6 +19,7 @@ along with this program. If not, see package apis import ( + "sort" "time" "github.com/cgrates/birpc/context" @@ -28,7 +29,7 @@ import ( ) // GetRateProfile returns a Rate Profile based on tenant and id -func (admS *AdminSv1) GetRateProfile(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *utils.RateProfile) error { +func (admS *AdminSv1) GetRateProfile(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...) } @@ -36,16 +37,48 @@ func (admS *AdminSv1) GetRateProfile(ctx *context.Context, arg *utils.TenantIDWi if tnt == utils.EmptyString { tnt = admS.cfg.GeneralCfg().DefaultTenant } - rPrf, err := admS.dm.GetRateProfile(ctx, tnt, arg.ID, true, true, utils.NonTransactional) + 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 err + return } - + rateIDs := make([]string, len(rPrf.Rates)) + i := 0 + for rateID := range rPrf.Rates { + rateIDs[i] = rateID + i++ + } + sort.Strings(rateIDs) + var limit, offset, maxItems int + // if limit, err = engine.GetIntOpts(ctx, tnt, nil, nil, admS.cfg.RateSCfg().Opts.Limit, + // config.RatesLimitDftOpt, utils.OptsRatesLimit); err != nil { + // return + // } + // if offset, err = engine.GetIntOpts(ctx, tnt, nil, nil, admS.cfg.RateSCfg().Opts.Offset, + // config.RatesLimitDftOpt, utils.OptsRatesOffset); err != nil { + // return + // } + // if maxItems, err = engine.GetIntOpts(ctx, tnt, nil, nil, admS.cfg.RateSCfg().Opts.Limit, + // config.RatesLimitDftOpt, utils.OptsRatesMaxItems); err != nil { + // return + // } + if limit, offset, maxItems, err = utils.GetPaginateOpts(arg.APIOpts); err != nil { + return + } + rateIDs, err = utils.Paginate(rateIDs, limit, offset, maxItems) + if err != nil { + return + } + paginatedRates := make(map[string]*utils.Rate) + for _, rateID := range rateIDs { + paginatedRates[rateID] = rPrf.Rates[rateID] + } + rPrf.Rates = paginatedRates *reply = *rPrf - return nil + return } // GetRateProfile returns the rates of a profile based on their profile. Those rates will be returned back by matching a prefix. diff --git a/config/config_defaults.go b/config/config_defaults.go index b60483e81..5a45319ac 100644 --- a/config/config_defaults.go +++ b/config/config_defaults.go @@ -1564,7 +1564,28 @@ const CGRATES_CFG_JSON = ` // "FilterIDs": [], // "Value": false, // }, - ], + ], + // "*limit": [ + // { + // "Tenant": "*any", + // "FilterIDs": [], + // "Value": 1, + // }, + // ], + // "*offset": [ + // { + // "Tenant": "*any", + // "FilterIDs": [], + // "Value": 1, + // }, + // ], + // "*maxItems": [ + // { + // "Tenant": "*any", + // "FilterIDs": [], + // "Value": 1, + // }, + // ], }, }, diff --git a/config/ratescfg.go b/config/ratescfg.go index e198c7548..db760a289 100644 --- a/config/ratescfg.go +++ b/config/ratescfg.go @@ -35,6 +35,9 @@ var ( const ( RatesStartTimeDftOpt = "*now" RatesProfileIgnoreFiltersDftOpt = false + RatesLimitDftOpt = 0 + RatesOffsetDftOpt = 0 + RatesMaxItemsDftOpt = 0 ) type RatesOpts struct { @@ -43,6 +46,9 @@ type RatesOpts struct { Usage []*utils.DynamicDecimalBigOpt IntervalStart []*utils.DynamicDecimalBigOpt ProfileIgnoreFilters []*utils.DynamicBoolOpt + Limit []*utils.DynamicIntOpt + Offset []*utils.DynamicIntOpt + MaxItems []*utils.DynamicIntOpt } // RateSCfg the rates config section @@ -102,6 +108,15 @@ func (rateOpts *RatesOpts) loadFromJSONCfg(jsnCfg *RatesOptsJson) (err error) { if jsnCfg.ProfileIgnoreFilters != nil { rateOpts.ProfileIgnoreFilters = append(rateOpts.ProfileIgnoreFilters, jsnCfg.ProfileIgnoreFilters...) } + if jsnCfg.Limit != nil { + rateOpts.Limit = append(rateOpts.Limit, jsnCfg.Limit...) + } + if jsnCfg.Offset != nil { + rateOpts.Offset = append(rateOpts.Offset, jsnCfg.Offset...) + } + if jsnCfg.MaxItems != nil { + rateOpts.MaxItems = append(rateOpts.MaxItems, jsnCfg.MaxItems...) + } return } @@ -172,6 +187,9 @@ func (rCfg RateSCfg) AsMapInterface(string) interface{} { utils.MetaUsage: rCfg.Opts.Usage, utils.MetaIntervalStartCfg: rCfg.Opts.IntervalStart, utils.MetaProfileIgnoreFilters: rCfg.Opts.ProfileIgnoreFilters, + utils.MetaLimitCfg: rCfg.Opts.Limit, + utils.MetaOffsetCfg: rCfg.Opts.Offset, + utils.MetaMaxItemsCfg: rCfg.Opts.MaxItems, } mp := map[string]interface{}{ utils.EnabledCfg: rCfg.Enabled, @@ -239,12 +257,27 @@ func (rateOpts *RatesOpts) Clone() *RatesOpts { if rateOpts.ProfileIgnoreFilters != nil { profileIgnoreFilters = utils.CloneDynamicBoolOpt(rateOpts.ProfileIgnoreFilters) } + var limit []*utils.DynamicIntOpt + if rateOpts.Limit != nil { + limit = utils.CloneDynamicIntOpt(rateOpts.Limit) + } + var offset []*utils.DynamicIntOpt + if rateOpts.Offset != nil { + offset = utils.CloneDynamicIntOpt(rateOpts.Offset) + } + var maxItems []*utils.DynamicIntOpt + if rateOpts.MaxItems != nil { + maxItems = utils.CloneDynamicIntOpt(rateOpts.MaxItems) + } return &RatesOpts{ ProfileIDs: ratePrfIDs, StartTime: startTime, Usage: usage, IntervalStart: intervalStart, ProfileIgnoreFilters: profileIgnoreFilters, + Limit: limit, + Offset: offset, + MaxItems: maxItems, } } @@ -298,6 +331,9 @@ type RatesOptsJson struct { Usage []*utils.DynamicStringOpt `json:"*usage"` IntervalStart []*utils.DynamicStringOpt `json:"*intervalStart"` ProfileIgnoreFilters []*utils.DynamicBoolOpt `json:"*profileIgnoreFilters"` + Limit []*utils.DynamicIntOpt `json:"*limit"` + Offset []*utils.DynamicIntOpt `json:"*offset"` + MaxItems []*utils.DynamicIntOpt `json:"*maxItems"` } type RateSJsonCfg struct { @@ -339,6 +375,15 @@ func diffRatesOptsJsonCfg(d *RatesOptsJson, v1, v2 *RatesOpts) *RatesOptsJson { if !utils.DynamicBoolOptEqual(v1.ProfileIgnoreFilters, v2.ProfileIgnoreFilters) { d.ProfileIgnoreFilters = v2.ProfileIgnoreFilters } + if !utils.DynamicIntOptEqual(v1.Limit, v2.Limit) { + d.Limit = v2.Limit + } + if !utils.DynamicIntOptEqual(v1.Offset, v2.Offset) { + d.Offset = v2.Offset + } + if !utils.DynamicIntOptEqual(v1.MaxItems, v2.MaxItems) { + d.MaxItems = v2.MaxItems + } return d } diff --git a/utils/consts.go b/utils/consts.go index 619729285..0266db7f1 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -2281,6 +2281,9 @@ const ( OptsRatesStartTime = "*rtsStartTime" OptsRatesUsage = "*rtsUsage" OptsRatesIntervalStart = "*rtsIntervalStart" + OptsRatesLimit = "*rtsLimit" + OptsRatesOffset = "*rtsOffset" + OptsRatesMaxItems = "*rtsMaxItems" // Resources OptsResourcesUnits = "*rsUnits"