diff --git a/pkg/cloudcommon/db/db_dispatcher.go b/pkg/cloudcommon/db/db_dispatcher.go index e19295e1ba..427f6dfb48 100644 --- a/pkg/cloudcommon/db/db_dispatcher.go +++ b/pkg/cloudcommon/db/db_dispatcher.go @@ -433,26 +433,57 @@ func listItems(manager IModelManager, ctx context.Context, userCred mcclient.Tok q = q.Desc(orderByField) } } - if limit > 0 { - q = q.Limit(int(limit)) + customizeFilters, err := manager.CustomizeFilterList(ctx, q, userCred, queryDict) + if err != nil { + return nil, err } - if offset > 0 { - q = q.Offset(int(offset)) + if customizeFilters.IsEmpty() { + if limit > 0 { + q = q.Limit(int(limit)) + } + if offset > 0 { + q = q.Offset(int(offset)) + } } retList, err := query2List(manager, ctx, userCred, q, queryDict) if err != nil { return nil, httperrors.NewGeneralError(err) } retConut := len(retList) - retList, err = manager.CustomizeFilterList(ctx, userCred, queryDict, retList) + + // apply customizeFilters + retList, err = customizeFilters.DoApply(retList) if err != nil { return nil, httperrors.NewGeneralError(err) } if len(retList) != retConut { totalCnt = int64(len(retList)) } - retResult := modules.ListResult{Data: retList, Total: int(totalCnt), Limit: int(limit), Offset: int(offset)} - return &retResult, nil + paginate := false + if !customizeFilters.IsEmpty() { + // query not use Limit and Offset, do manual pagination + paginate = true + } + return calculateListResult(retList, totalCnt, limit, offset, paginate), nil +} + +func calculateListResult(data []jsonutils.JSONObject, total, limit, offset int64, paginate bool) *modules.ListResult { + if paginate { + // do offset first + if offset != 0 { + if total > offset { + data = data[offset:] + } else { + data = []jsonutils.JSONObject{} + } + } + // do limit + if total > limit { + data = data[:limit] + } + } + retResult := modules.ListResult{Data: data, Total: int(total), Limit: int(limit), Offset: int(offset)} + return &retResult } func (dispatcher *DBModelDispatcher) List(ctx context.Context, query jsonutils.JSONObject, ctxId string) (*modules.ListResult, error) { diff --git a/pkg/cloudcommon/db/interface.go b/pkg/cloudcommon/db/interface.go index 9866b6dcdc..2262cebe28 100644 --- a/pkg/cloudcommon/db/interface.go +++ b/pkg/cloudcommon/db/interface.go @@ -29,7 +29,7 @@ type IModelManager interface { AllowListItems(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool ValidateListConditions(ctx context.Context, userCred mcclient.TokenCredential, query *jsonutils.JSONDict) (*jsonutils.JSONDict, error) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) - CustomizeFilterList(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, retList []jsonutils.JSONObject) ([]jsonutils.JSONObject, error) + CustomizeFilterList(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*CustomizeListFilters, error) ExtraSearchConditions(ctx context.Context, q *sqlchemy.SQuery, like string) []sqlchemy.ICondition // fetch hook diff --git a/pkg/cloudcommon/db/list.go b/pkg/cloudcommon/db/list.go new file mode 100644 index 0000000000..a6b37d4659 --- /dev/null +++ b/pkg/cloudcommon/db/list.go @@ -0,0 +1,60 @@ +package db + +import ( + "yunion.io/x/jsonutils" +) + +type CustomizeListFilterFunc func(item jsonutils.JSONObject) (bool, error) + +type CustomizeListFilters struct { + filters []CustomizeListFilterFunc +} + +func NewCustomizeListFilters() *CustomizeListFilters { + return &CustomizeListFilters{ + filters: []CustomizeListFilterFunc{}, + } +} + +func (f *CustomizeListFilters) Append(funcs ...CustomizeListFilterFunc) *CustomizeListFilters { + f.filters = append(f.filters, funcs...) + return f +} + +func (f CustomizeListFilters) Len() int { + return len(f.filters) +} + +func (f CustomizeListFilters) IsEmpty() bool { + return f.Len() == 0 +} + +func (f CustomizeListFilters) DoApply(objs []jsonutils.JSONObject) ([]jsonutils.JSONObject, error) { + filteredObjs := []jsonutils.JSONObject{} + for _, obj := range objs { + ok, err := f.singleApply(obj) + if err != nil { + return nil, err + } + if ok { + filteredObjs = append(filteredObjs, obj) + } + } + return filteredObjs, nil +} + +func (f CustomizeListFilters) singleApply(obj jsonutils.JSONObject) (bool, error) { + if f.IsEmpty() { + return true, nil + } + for _, filter := range f.filters { + ok, err := filter(obj) + if err != nil { + return false, err + } + if !ok { + return false, nil + } + } + return true, nil +} diff --git a/pkg/cloudcommon/db/modelbase.go b/pkg/cloudcommon/db/modelbase.go index 0cfdec9dbc..14c3090680 100644 --- a/pkg/cloudcommon/db/modelbase.go +++ b/pkg/cloudcommon/db/modelbase.go @@ -75,8 +75,8 @@ func (manager *SModelBaseManager) ListItemFilter(ctx context.Context, q *sqlchem return q, nil } -func (manager *SModelBaseManager) CustomizeFilterList(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, retList []jsonutils.JSONObject) ([]jsonutils.JSONObject, error) { - return retList, nil +func (manager *SModelBaseManager) CustomizeFilterList(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*CustomizeListFilters, error) { + return NewCustomizeListFilters(), nil } func (manager *SModelBaseManager) ExtraSearchConditions(ctx context.Context, q *sqlchemy.SQuery, like string) []sqlchemy.ICondition { diff --git a/pkg/compute/models/networks.go b/pkg/compute/models/networks.go index 1d5d4f0e19..62737711e5 100644 --- a/pkg/compute/models/networks.go +++ b/pkg/compute/models/networks.go @@ -1291,28 +1291,28 @@ func (self *SNetwork) isManaged() bool { } } -func (manager *SNetworkManager) CustomizeFilterList(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, retList []jsonutils.JSONObject) ([]jsonutils.JSONObject, error) { +func (manager *SNetworkManager) CustomizeFilterList(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*db.CustomizeListFilters, error) { + filters := db.NewCustomizeListFilters() + if query.Contains("ip") { ip, _ := query.GetString("ip") ipInt, err := netutils.NewIPV4Addr(ip) if err != nil { return nil, err } - ret := make([]jsonutils.JSONObject, 0) - for i := 0; i < len(retList); i++ { - guestIpStart, _ := retList[i].GetString("guest_ip_start") - guestIpEnd, _ := retList[i].GetString("guest_ip_end") + + ipFilter := func(obj jsonutils.JSONObject) (bool, error) { + guestIpStart, _ := obj.GetString("guest_ip_start") + guestIpEnd, _ := obj.GetString("guest_ip_end") guestIpStartInt, _ := netutils.NewIPV4Addr(guestIpStart) guestIpEndInt, _ := netutils.NewIPV4Addr(guestIpEnd) ipRange := netutils.NewIPV4AddrRange(guestIpStartInt, guestIpEndInt) - if ipRange.Contains(ipInt) { - ret = append(ret, retList[i]) - } + return ipRange.Contains(ipInt), nil } - return ret, nil - } else { - return manager.SStatusStandaloneResourceBaseManager.CustomizeFilterList(ctx, userCred, query, retList) + + filters.Append(ipFilter) } + return filters, nil } func (manager *SNetworkManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) {