Skip to content

Commit

Permalink
refactor: refactor list things
Browse files Browse the repository at this point in the history
Signed-off-by: 1998-felix <felix.gateru@gmail.com>
  • Loading branch information
felixgateru committed Aug 26, 2024
1 parent 466ea3a commit 46669f6
Show file tree
Hide file tree
Showing 5 changed files with 419 additions and 116 deletions.
10 changes: 5 additions & 5 deletions api/openapi/things.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ paths:
operationId: listThings
tags:
- Things
summary: Retrieves things
summary: Retrieves a list of things.
description: |
Retrieves a list of things. Due to performance concerns, data
is retrieved in subsets. The API things must ensure that the entire
Expand Down Expand Up @@ -1559,7 +1559,7 @@ components:
schema:
type: string
format: uuid
required: true
required: false
example: bb7edb32-2eac-4aad-aebe-ed96fe073879

User:
Expand All @@ -1569,7 +1569,7 @@ components:
schema:
type: string
format: uuid
required: true
required: false
example: bb7edb32-2eac-4aad-aebe-ed96fe073879

Group:
Expand All @@ -1579,7 +1579,7 @@ components:
schema:
type: string
format: uuid
required: true
required: false
example: bb7edb32-2eac-4aad-aebe-ed96fe073879

Thing:
Expand All @@ -1589,7 +1589,7 @@ components:
schema:
type: string
format: uuid
required: true
required: false
example: bb7edb32-2eac-4aad-aebe-ed96fe073879

ParentId:
Expand Down
15 changes: 9 additions & 6 deletions pkg/clients/postgres/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,11 @@ func (repo *Repository) RetrieveAll(ctx context.Context, pm clients.Page) (clien
if err != nil {
return clients.ClientsPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
}
tq := query
query = applyOrdering(query, pm)

q := fmt.Sprintf(`SELECT c.id, c.name, c.tags, c.identity, c.metadata, COALESCE(c.domain_id, '') AS domain_id, c.status,
c.created_at, c.updated_at, COALESCE(c.updated_by, '') AS updated_by FROM clients c %s ORDER BY c.created_at LIMIT :limit OFFSET :offset;`, query)
c.created_at, c.updated_at, COALESCE(c.updated_by, '') AS updated_by FROM clients c %s LIMIT :limit OFFSET :offset;`, query)

dbPage, err := ToDBClientsPage(pm)
if err != nil {
Expand All @@ -172,7 +173,7 @@ func (repo *Repository) RetrieveAll(ctx context.Context, pm clients.Page) (clien

items = append(items, c)
}
cq := fmt.Sprintf(`SELECT COUNT(*) FROM clients c %s;`, query)
cq := fmt.Sprintf(`SELECT COUNT(*) FROM clients c %s;`, tq)

total, err := postgres.Total(ctx, repo.DB, cq, dbPage)
if err != nil {
Expand Down Expand Up @@ -522,11 +523,13 @@ func PageQuery(pm clients.Page) (string, error) {

func applyOrdering(emq string, pm clients.Page) string {
switch pm.Order {
case "name", "identity", "created_at", "updated_at":
case "name", "identity", "updated_at":
emq = fmt.Sprintf("%s ORDER BY %s", emq, pm.Order)
if pm.Dir == api.AscDir || pm.Dir == api.DescDir {
emq = fmt.Sprintf("%s %s", emq, pm.Dir)
}
default:
emq = fmt.Sprintf("%s ORDER BY %s", emq, "created_at")
}
if pm.Dir == api.AscDir || pm.Dir == api.DescDir {
emq = fmt.Sprintf("%s %s", emq, pm.Dir)
}
return emq
}
79 changes: 37 additions & 42 deletions things/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,52 +148,61 @@ func (svc service) ListClients(ctx context.Context, token string, pm mgclients.P
}

switch pm.EntityType {
// /things?user=<userID>
// check request user is viewer of domain in order to list things of particular user.
case auth.UserType:
if _, err := svc.authorize(ctx, "", auth.UserType, auth.UsersKind, res.GetId(), auth.ViewPermission, auth.DomainType, res.GetDomainId()); err != nil {
return mgclients.ClientsPage{}, err
}
pm.EntityID = auth.EncodeDomainUserID(res.GetDomainId(), pm.EntityID)
// /things?channel=<channelID>
// check request user have view permission on channel;
return svc.listEntityThings(ctx, res.GetId(), res.GetDomainId(), auth.ViewPermission, auth.DomainType, res.GetDomainId(), pm)
case auth.GroupType:
if _, err := svc.authorize(ctx, "", auth.UserType, auth.UsersKind, res.GetId(), auth.ViewPermission, auth.GroupType, pm.EntityID); err != nil {
return mgclients.ClientsPage{}, err
}
// /things
// List things of the request user
pm.Permission = auth.GroupRelation
return svc.listEntityThings(ctx, res.GetId(), res.GetDomainId(), auth.ViewPermission, auth.GroupType, pm.EntityID, pm)
case "":
_, err := svc.authorize(ctx, "", auth.UserType, auth.UsersKind, res.GetId(), auth.ViewPermission, auth.DomainType, res.GetDomainId())
switch {
case err == nil:
pm.Domain = res.GetDomainId()
default:
// If domain is disabled , then this authorization will fail for all non-admin domain users
if _, err := svc.authorize(ctx, "", auth.UserType, auth.UsersKind, res.GetId(), auth.MembershipPermission, auth.DomainType, res.GetDomainId()); err != nil {
return mgclients.ClientsPage{}, err
}
pm.EntityType = auth.UserType
pm.EntityID = res.GetId()
return svc.listEntityThings(ctx, res.GetId(), res.GetDomainId(), auth.MembershipPermission, auth.DomainType, res.GetDomainId(), pm)
}

default:
return mgclients.ClientsPage{}, errors.Wrap(svcerr.ErrMalformedEntity, fmt.Errorf("invalid entity type %s", pm.EntityType))
}
return svc.listThings(ctx, res.GetId(), pm)
}

// pm.EntityType will be not empty if entity type is user or channel or if request user is not viewer of domain.
if pm.EntityType != "" {
thingIDs, err := svc.listThings(ctx, res.GetDomainId(), pm.EntityType, pm.EntityID, pm.Permission)
if err != nil {
return mgclients.ClientsPage{}, err
}
pm.IDs = thingIDs
func (svc service) listEntityThings(ctx context.Context, userID, domainID, permission, objectType, object string, pm mgclients.Page) (mgclients.ClientsPage, error) {
// If domain is disabled , then this authorization will fail for all non-admin domain users
if _, err := svc.authorize(ctx, domainID, auth.UserType, auth.UsersKind, userID, permission, objectType, object); err != nil {
return mgclients.ClientsPage{}, err
}

if len(pm.IDs) == 0 && pm.Domain == "" {
return mgclients.ClientsPage{}, nil
thingIDs, err := svc.listThingIDs(ctx, domainID, pm.EntityType, pm.EntityID, pm.Permission)
if err != nil {
return mgclients.ClientsPage{}, err
}
tp, err := svc.clients.SearchClients(ctx, pm)
if len(thingIDs) == 0 {
return mgclients.ClientsPage{Page: pm}, err
}
pm.IDs = thingIDs
return svc.listThings(ctx, userID, pm)
}

func (svc service) listThingIDs(ctx context.Context, domainID, entityType, entityID, permission string) ([]string, error) {
dtids, err := svc.auth.ListAllObjects(ctx, &magistrala.ListObjectsReq{
Domain: domainID,
SubjectType: entityType,
Subject: entityID,
Permission: permission,
ObjectType: auth.ThingType,
})
if err != nil {
return []string{}, errors.Wrap(svcerr.ErrViewEntity, err)
}
return dtids.Policies, nil
}

func (svc service) listThings(ctx context.Context, userID string, pm mgclients.Page) (mgclients.ClientsPage, error) {
tp, err := svc.clients.RetrieveAll(ctx, pm)
if err != nil {
return mgclients.ClientsPage{}, errors.Wrap(svcerr.ErrViewEntity, err)
}
Expand All @@ -205,7 +214,7 @@ func (svc service) ListClients(ctx context.Context, token string, pm mgclients.P
// Copying loop variable "i" to avoid "loop variable captured by func literal"
iter := i
g.Go(func() error {
return svc.retrievePermissions(ctx, res.GetId(), &tp.Clients[iter])
return svc.retrievePermissions(ctx, userID, &tp.Clients[iter])
})
}

Expand All @@ -216,20 +225,6 @@ func (svc service) ListClients(ctx context.Context, token string, pm mgclients.P
return tp, nil
}

func (svc service) listThings(ctx context.Context, domainID, entityType, entityID, permission string) ([]string, error) {
dtids, err := svc.auth.ListAllObjects(ctx, &magistrala.ListObjectsReq{
Domain: domainID,
SubjectType: entityType,
Subject: entityID,
Permission: permission,
ObjectType: auth.ThingType,
})
if err != nil {
return []string{}, errors.Wrap(svcerr.ErrViewEntity, err)
}
return dtids.Policies, nil
}

// Experimental functions used for async calling of svc.listUserThingPermission. This might be helpful during listing of large number of entities.
func (svc service) retrievePermissions(ctx context.Context, userID string, client *mgclients.Client) error {
permissions, err := svc.listUserThingPermission(ctx, userID, client.ID)
Expand Down
Loading

0 comments on commit 46669f6

Please sign in to comment.