SMQ-2840 - Retrieve entity total with filter options (#2961)

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>
This commit is contained in:
Steve Munene
2025-07-10 18:36:31 +03:00
committed by GitHub
parent da319a1aa9
commit 376bc8b9c4
24 changed files with 212 additions and 144 deletions
+1
View File
@@ -26,6 +26,7 @@ const (
DirKey = "dir"
OrderKey = "order"
LimitKey = "limit"
OnlyTotal = "only_total"
NameOrder = "name"
IDOrder = "id"
+5
View File
@@ -145,6 +145,10 @@ func decodeListChannels(_ context.Context, r *http.Request) (interface{}, error)
if err != nil {
return listChannelsReq{}, errors.Wrap(apiutil.ErrValidation, err)
}
ot, err := apiutil.ReadBoolQuery(r, api.OnlyTotal, false)
if err != nil {
return listChannelsReq{}, errors.Wrap(apiutil.ErrValidation, err)
}
req := listChannelsReq{
Page: channels.Page{
@@ -163,6 +167,7 @@ func decodeListChannels(_ context.Context, r *http.Request) (interface{}, error)
Group: groupID,
Client: clientID,
ID: id,
OnlyTotal: ot,
},
userID: userID,
}
+2 -2
View File
@@ -26,7 +26,7 @@ var (
type pageRes struct {
Limit uint64 `json:"limit,omitempty"`
Offset uint64 `json:"offset"`
Offset uint64 `json:"offset,omitempty"`
Total uint64 `json:"total"`
}
@@ -75,7 +75,7 @@ func (res viewChannelRes) Empty() bool {
type channelsPageRes struct {
pageRes
Channels []viewChannelRes `json:"channels"`
Channels []viewChannelRes `json:"channels,omitempty"`
}
func (res channelsPageRes) Code() int {
+1
View File
@@ -50,6 +50,7 @@ type Page struct {
Total uint64 `json:"total"`
Offset uint64 `json:"offset"`
Limit uint64 `json:"limit"`
OnlyTotal bool `json:"only_total"`
Order string `json:"order,omitempty"`
Dir string `json:"dir,omitempty"`
ID string `json:"id,omitempty"`
+33 -28
View File
@@ -432,25 +432,28 @@ func (cr *channelRepository) RetrieveAll(ctx context.Context, pm channels.Page)
if err != nil {
return channels.ChannelsPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
}
rows, err := cr.db.NamedQueryContext(ctx, q, dbPage)
if err != nil {
return channels.ChannelsPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
}
defer rows.Close()
var items []channels.Channel
for rows.Next() {
dbch := dbChannel{}
if err := rows.StructScan(&dbch); err != nil {
return channels.ChannelsPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
}
ch, err := toChannel(dbch)
if !pm.OnlyTotal {
rows, err := cr.db.NamedQueryContext(ctx, q, dbPage)
if err != nil {
return channels.ChannelsPage{}, err
return channels.ChannelsPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
}
defer rows.Close()
items = append(items, ch)
for rows.Next() {
dbch := dbChannel{}
if err := rows.StructScan(&dbch); err != nil {
return channels.ChannelsPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
}
ch, err := toChannel(dbch)
if err != nil {
return channels.ChannelsPage{}, err
}
items = append(items, ch)
}
}
cq := fmt.Sprintf(`SELECT COUNT(*) AS total_count
FROM (
@@ -546,25 +549,27 @@ func (repo *channelRepository) retrieveChannels(ctx context.Context, domainID, u
return channels.ChannelsPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
}
rows, err := repo.db.NamedQueryContext(ctx, q, dbPage)
if err != nil {
return channels.ChannelsPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
}
defer rows.Close()
var items []channels.Channel
for rows.Next() {
dbc := dbChannel{}
if err := rows.StructScan(&dbc); err != nil {
if !pm.OnlyTotal {
rows, err := repo.db.NamedQueryContext(ctx, q, dbPage)
if err != nil {
return channels.ChannelsPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
}
defer rows.Close()
c, err := toChannel(dbc)
if err != nil {
return channels.ChannelsPage{}, err
for rows.Next() {
dbc := dbChannel{}
if err := rows.StructScan(&dbc); err != nil {
return channels.ChannelsPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
}
c, err := toChannel(dbc)
if err != nil {
return channels.ChannelsPage{}, err
}
items = append(items, c)
}
items = append(items, c)
}
cq := fmt.Sprintf(`%s
+5
View File
@@ -131,6 +131,10 @@ func decodeListClients(_ context.Context, r *http.Request) (interface{}, error)
if err != nil {
return listClientsReq{}, errors.Wrap(apiutil.ErrValidation, err)
}
ot, err := apiutil.ReadBoolQuery(r, api.OnlyTotal, false)
if err != nil {
return listClientsReq{}, errors.Wrap(apiutil.ErrValidation, err)
}
req := listClientsReq{
Page: clients.Page{
@@ -150,6 +154,7 @@ func decodeListClients(_ context.Context, r *http.Request) (interface{}, error)
Channel: channelID,
ConnectionType: connType,
ID: id,
OnlyTotal: ot,
},
userID: userID,
}
+2 -2
View File
@@ -22,7 +22,7 @@ var (
type clientsPageMetaRes struct {
Limit uint64 `json:"limit,omitempty"`
Offset uint64 `json:"offset"`
Offset uint64 `json:"offset,omitempty"`
Total uint64 `json:"total"`
}
@@ -103,7 +103,7 @@ func (res viewClientPermsRes) Empty() bool {
type clientsPageRes struct {
clientsPageMetaRes
Clients []viewClientRes `json:"clients"`
Clients []viewClientRes `json:"clients,omitempty"`
}
func (res clientsPageRes) Code() int {
+1
View File
@@ -198,6 +198,7 @@ type Page struct {
Total uint64 `json:"total"`
Offset uint64 `json:"offset"`
Limit uint64 `json:"limit"`
OnlyTotal bool `json:"only_total"`
Order string `json:"order,omitempty"`
Dir string `json:"dir,omitempty"`
ID string `json:"id,omitempty"`
+33 -29
View File
@@ -456,25 +456,27 @@ func (repo *clientRepo) RetrieveAll(ctx context.Context, pm clients.Page) (clien
if err != nil {
return clients.ClientsPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
}
rows, err := repo.DB.NamedQueryContext(ctx, q, dbPage)
if err != nil {
return clients.ClientsPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
}
defer rows.Close()
var items []clients.Client
for rows.Next() {
dbc := DBClient{}
if err := rows.StructScan(&dbc); err != nil {
return clients.ClientsPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
}
c, err := ToClient(dbc)
if !pm.OnlyTotal {
rows, err := repo.DB.NamedQueryContext(ctx, q, dbPage)
if err != nil {
return clients.ClientsPage{}, err
return clients.ClientsPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
}
defer rows.Close()
items = append(items, c)
for rows.Next() {
dbc := DBClient{}
if err := rows.StructScan(&dbc); err != nil {
return clients.ClientsPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
}
c, err := ToClient(dbc)
if err != nil {
return clients.ClientsPage{}, err
}
items = append(items, c)
}
}
cq := fmt.Sprintf(`SELECT COUNT(*) AS total_count
FROM (
@@ -571,25 +573,27 @@ func (repo *clientRepo) retrieveClients(ctx context.Context, domainID, userID st
return clients.ClientsPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
}
rows, err := repo.DB.NamedQueryContext(ctx, q, dbPage)
if err != nil {
return clients.ClientsPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
}
defer rows.Close()
var items []clients.Client
for rows.Next() {
dbc := DBClient{}
if err := rows.StructScan(&dbc); err != nil {
if !pm.OnlyTotal {
rows, err := repo.DB.NamedQueryContext(ctx, q, dbPage)
if err != nil {
return clients.ClientsPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
}
defer rows.Close()
c, err := ToClient(dbc)
if err != nil {
return clients.ClientsPage{}, err
for rows.Next() {
dbc := DBClient{}
if err := rows.StructScan(&dbc); err != nil {
return clients.ClientsPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
}
c, err := ToClient(dbc)
if err != nil {
return clients.ClientsPage{}, err
}
items = append(items, c)
}
items = append(items, c)
}
cq := fmt.Sprintf(`%s
+23 -12
View File
@@ -162,19 +162,25 @@ func decodePageRequest(_ context.Context, r *http.Request) (domains.Page, error)
return domains.Page{}, errors.Wrap(apiutil.ErrValidation, err)
}
ot, err := apiutil.ReadBoolQuery(r, api.OnlyTotal, false)
if err != nil {
return domains.Page{}, errors.Wrap(apiutil.ErrValidation, err)
}
return domains.Page{
Offset: o,
Order: or,
Dir: dir,
Limit: l,
Name: n,
Metadata: m,
Tag: t,
RoleID: roleID,
RoleName: roleName,
Actions: actions,
Status: st,
ID: id,
Offset: o,
Order: or,
Dir: dir,
Limit: l,
Name: n,
Metadata: m,
Tag: t,
RoleID: roleID,
RoleName: roleName,
Actions: actions,
Status: st,
ID: id,
OnlyTotal: ot,
}, nil
}
@@ -224,6 +230,10 @@ func decodeListInvitationsReq(_ context.Context, r *http.Request) (interface{},
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
ot, err := apiutil.ReadBoolQuery(r, api.OnlyTotal, false)
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
req := listInvitationsReq{
InvitationPageMeta: domains.InvitationPageMeta{
Offset: offset,
@@ -233,6 +243,7 @@ func decodeListInvitationsReq(_ context.Context, r *http.Request) (interface{},
RoleID: roleID,
DomainID: domainID,
State: state,
OnlyTotal: ot,
},
}
+20 -19
View File
@@ -124,29 +124,30 @@ type Domain struct {
}
type Page struct {
Total uint64 `json:"total"`
Offset uint64 `json:"offset"`
Limit uint64 `json:"limit"`
Name string `json:"name,omitempty"`
Order string `json:"-"`
Dir string `json:"-"`
Metadata Metadata `json:"metadata,omitempty"`
Tag string `json:"tag,omitempty"`
RoleName string `json:"role_name,omitempty"`
RoleID string `json:"role_id,omitempty"`
Actions []string `json:"actions,omitempty"`
Status Status `json:"status,omitempty"`
ID string `json:"id,omitempty"`
IDs []string `json:"-"`
Identity string `json:"identity,omitempty"`
UserID string `json:"user_id,omitempty"`
Total uint64 `json:"total"`
Offset uint64 `json:"offset"`
Limit uint64 `json:"limit"`
OnlyTotal bool `json:"only_total"`
Name string `json:"name,omitempty"`
Order string `json:"-"`
Dir string `json:"-"`
Metadata Metadata `json:"metadata,omitempty"`
Tag string `json:"tag,omitempty"`
RoleName string `json:"role_name,omitempty"`
RoleID string `json:"role_id,omitempty"`
Actions []string `json:"actions,omitempty"`
Status Status `json:"status,omitempty"`
ID string `json:"id,omitempty"`
IDs []string `json:"-"`
Identity string `json:"identity,omitempty"`
UserID string `json:"user_id,omitempty"`
}
type DomainsPage struct {
Total uint64 `json:"total"`
Offset uint64 `json:"offset"`
Limit uint64 `json:"limit"`
Domains []Domain `json:"domains"`
Offset uint64 `json:"offset,omitempty"`
Limit uint64 `json:"limit,omitempty"`
Domains []Domain `json:"domains,omitempty"`
}
func (page DomainsPage) MarshalJSON() ([]byte, error) {
+1
View File
@@ -50,6 +50,7 @@ func (page InvitationPage) MarshalJSON() ([]byte, error) {
type InvitationPageMeta struct {
Offset uint64 `json:"offset" db:"offset"`
Limit uint64 `json:"limit" db:"limit"`
OnlyTotal bool `json:"only_total"`
InvitedBy string `json:"invited_by,omitempty" db:"invited_by,omitempty"`
InviteeUserID string `json:"invitee_user_id,omitempty" db:"invitee_user_id,omitempty"`
DomainID string `json:"domain_id,omitempty" db:"domain_id,omitempty"`
+11 -8
View File
@@ -349,15 +349,18 @@ func (repo domainRepo) ListDomains(ctx context.Context, pm domains.Page) (domain
return domains.DomainsPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
}
rows, err := repo.db.NamedQueryContext(ctx, q, dbPage)
if err != nil {
return domains.DomainsPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
}
defer rows.Close()
var doms []domains.Domain
if !pm.OnlyTotal {
rows, err := repo.db.NamedQueryContext(ctx, q, dbPage)
if err != nil {
return domains.DomainsPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
}
defer rows.Close()
doms, err := repo.processRows(rows)
if err != nil {
return domains.DomainsPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
doms, err = repo.processRows(rows)
if err != nil {
return domains.DomainsPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
}
}
cq := `SELECT COUNT(*)
+12 -10
View File
@@ -77,19 +77,21 @@ func (repo domainRepo) RetrieveAllInvitations(ctx context.Context, pm domains.In
LIMIT :limit OFFSET :offset;
`, query)
rows, err := repo.db.NamedQueryContext(ctx, q, pm)
if err != nil {
return domains.InvitationPage{}, postgres.HandleError(repoerr.ErrViewEntity, err)
}
defer rows.Close()
var items []domains.Invitation
for rows.Next() {
var dbinv dbInvitation
if err = rows.StructScan(&dbinv); err != nil {
if !pm.OnlyTotal {
rows, err := repo.db.NamedQueryContext(ctx, q, pm)
if err != nil {
return domains.InvitationPage{}, postgres.HandleError(repoerr.ErrViewEntity, err)
}
items = append(items, toInvitation(dbinv))
defer rows.Close()
for rows.Next() {
var dbinv dbInvitation
if err = rows.StructScan(&dbinv); err != nil {
return domains.InvitationPage{}, postgres.HandleError(repoerr.ErrViewEntity, err)
}
items = append(items, toInvitation(dbinv))
}
}
tq := fmt.Sprintf(`
+6
View File
@@ -279,6 +279,11 @@ func decodePageMeta(r *http.Request) (groups.PageMeta, error) {
return groups.PageMeta{}, errors.Wrap(apiutil.ErrValidation, err)
}
ot, err := apiutil.ReadBoolQuery(r, api.OnlyTotal, false)
if err != nil {
return groups.PageMeta{}, errors.Wrap(apiutil.ErrValidation, err)
}
ret := groups.PageMeta{
Offset: offset,
Limit: limit,
@@ -291,6 +296,7 @@ func decodePageMeta(r *http.Request) (groups.PageMeta, error) {
Actions: actions,
AccessType: accessType,
RootGroup: rootGroup,
OnlyTotal: ot,
}
return ret, nil
}
+2 -2
View File
@@ -71,12 +71,12 @@ func (res createGroupRes) Empty() bool {
type groupPageRes struct {
pageRes
Groups []viewGroupRes `json:"groups"`
Groups []viewGroupRes `json:"groups,omitempty"`
}
type pageRes struct {
Limit uint64 `json:"limit,omitempty"`
Offset uint64 `json:"offset"`
Offset uint64 `json:"offset,omitempty"`
Total uint64 `json:"total"`
}
+1
View File
@@ -8,6 +8,7 @@ type PageMeta struct {
Total uint64 `json:"total"`
Offset uint64 `json:"offset"`
Limit uint64 `json:"limit"`
OnlyTotal bool `json:"only_total"`
Name string `json:"name,omitempty"`
ID string `json:"id,omitempty"`
Path string `json:"path,omitempty"`
+24 -16
View File
@@ -418,15 +418,19 @@ func (repo groupRepository) RetrieveAll(ctx context.Context, pm groups.PageMeta)
if err != nil {
return groups.Page{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
}
rows, err := repo.db.NamedQueryContext(ctx, q, dbPageMeta)
if err != nil {
return groups.Page{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
}
defer rows.Close()
items, err := repo.processRows(rows)
if err != nil {
return groups.Page{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
var items []groups.Group
if !pm.OnlyTotal {
rows, err := repo.db.NamedQueryContext(ctx, q, dbPageMeta)
if err != nil {
return groups.Page{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
}
defer rows.Close()
items, err = repo.processRows(rows)
if err != nil {
return groups.Page{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
}
}
cq := fmt.Sprintf(` SELECT COUNT(*) AS total_count
@@ -864,15 +868,19 @@ func (repo groupRepository) retrieveGroups(ctx context.Context, domainID, userID
if err != nil {
return groups.Page{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
}
rows, err := repo.db.NamedQueryContext(ctx, q, dbPageMeta)
if err != nil {
return groups.Page{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
}
defer rows.Close()
items, err := repo.processRows(rows)
if err != nil {
return groups.Page{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
var items []groups.Group
if !pm.OnlyTotal {
rows, err := repo.db.NamedQueryContext(ctx, q, dbPageMeta)
if err != nil {
return groups.Page{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
}
defer rows.Close()
items, err = repo.processRows(rows)
if err != nil {
return groups.Page{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
}
}
cq := fmt.Sprintf(`%s
+1
View File
@@ -94,6 +94,7 @@ func listUsersEndpoint(svc users.Service) endpoint.Endpoint {
Status: req.status,
Offset: req.offset,
Limit: req.limit,
OnlyTotal: req.onlyTotal,
Username: req.userName,
Tag: req.tag,
Metadata: req.metadata,
+1
View File
@@ -83,6 +83,7 @@ type listUsersReq struct {
status users.Status
offset uint64
limit uint64
onlyTotal bool
userName string
tag string
firstName string
+2 -2
View File
@@ -30,7 +30,7 @@ var (
type pageRes struct {
Limit uint64 `json:"limit,omitempty"`
Offset uint64 `json:"offset"`
Offset uint64 `json:"offset,omitempty"`
Total uint64 `json:"total"`
}
@@ -113,7 +113,7 @@ func (res viewUserRes) Empty() bool {
type usersPageRes struct {
pageRes
Users []viewUserRes `json:"users"`
Users []viewUserRes `json:"users,omitempty"`
}
func (res usersPageRes) Code() int {
+7
View File
@@ -262,10 +262,17 @@ func decodeListUsers(_ context.Context, r *http.Request) (interface{}, error) {
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
ot, err := apiutil.ReadBoolQuery(r, api.OnlyTotal, false)
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
req := listUsersReq{
status: st,
offset: o,
limit: l,
onlyTotal: ot,
metadata: m,
userName: n,
firstName: i,
+17 -14
View File
@@ -125,25 +125,28 @@ func (repo *userRepo) RetrieveAll(ctx context.Context, pm users.Page) (users.Use
if err != nil {
return users.UsersPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
}
rows, err := repo.Repository.DB.NamedQueryContext(ctx, q, dbPage)
if err != nil {
return users.UsersPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
}
defer rows.Close()
var items []users.User
for rows.Next() {
dbu := DBUser{}
if err := rows.StructScan(&dbu); err != nil {
return users.UsersPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
}
c, err := ToUser(dbu)
if !pm.OnlyTotal {
rows, err := repo.Repository.DB.NamedQueryContext(ctx, q, dbPage)
if err != nil {
return users.UsersPage{}, err
return users.UsersPage{}, errors.Wrap(repoerr.ErrFailedToRetrieveAllGroups, err)
}
defer rows.Close()
items = append(items, c)
for rows.Next() {
dbu := DBUser{}
if err := rows.StructScan(&dbu); err != nil {
return users.UsersPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
}
c, err := ToUser(dbu)
if err != nil {
return users.UsersPage{}, err
}
items = append(items, c)
}
}
cq := fmt.Sprintf(`SELECT COUNT(*) FROM users u %s;`, query)
+1
View File
@@ -127,6 +127,7 @@ type Page struct {
Total uint64 `json:"total"`
Offset uint64 `json:"offset"`
Limit uint64 `json:"limit"`
OnlyTotal bool `json:"only_total"`
Id string `json:"id,omitempty"`
Order string `json:"order,omitempty"`
Dir string `json:"dir,omitempty"`