Skip to content
This repository has been archived by the owner on Oct 14, 2024. It is now read-only.

Commit

Permalink
fix: user role update, listing and retrieve
Browse files Browse the repository at this point in the history
Signed-off-by: Arvindh <[email protected]>
  • Loading branch information
arvindh123 committed Nov 27, 2023
1 parent b13307c commit dfcfe45
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 27 deletions.
24 changes: 11 additions & 13 deletions api/openapi/users.yml
Original file line number Diff line number Diff line change
Expand Up @@ -215,18 +215,17 @@ paths:
"500":
$ref: "#/components/responses/ServiceError"

/users/{userID}/owner:
/users/{userID}/role:
patch:
summary: Updates the user owner.
summary: Updates the user role.
description: |
Updates owner for the user with provided ID. Owner is updated using
authorization token and a new owner identifier received in request.
Updates role for the user with provided ID.
tags:
- Users
parameters:
- $ref: "#/components/parameters/UserID"
requestBody:
$ref: "#/components/requestBodies/UserUpdateOwnerReq"
$ref: "#/components/requestBodies/UserUpdateRoleReq"
security:
- bearerAuth: []
responses:
Expand Down Expand Up @@ -299,8 +298,6 @@ paths:
authorization token and the new received info.
tags:
- Users
parameters:
- $ref: "#/components/parameters/UserID"
requestBody:
$ref: "#/components/requestBodies/UserUpdateSecretReq"
security:
Expand Down Expand Up @@ -1264,13 +1261,14 @@ components:
- old_secret
- new_secret

UserOwner:
UserRole:
type: object
properties:
owner:
role:
type: string
example: [email protected]
description: User owner for example email address.
enum: ["admin","user"]
example: user
description: User role example.
required:
- owner

Expand Down Expand Up @@ -1658,13 +1656,13 @@ components:
schema:
$ref: "#/components/schemas/UserSecret"

UserUpdateOwnerReq:
UserUpdateRoleReq:
description: JSON-formated document describing the owner of user to be update
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/UserOwner"
$ref: "#/components/schemas/UserRole"

GroupCreateReq:
description: JSON-formatted document describing the new group to be registered
Expand Down
28 changes: 16 additions & 12 deletions pkg/clients/postgres/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,15 +138,15 @@ func (repo ClientRepository) RetrieveByIdentity(ctx context.Context, identity st
}

func (repo ClientRepository) RetrieveAll(ctx context.Context, pm clients.Page) (clients.ClientsPage, error) {
query, err := pageQuery(pm)
query, err := PageQuery(pm)
if err != nil {
return clients.ClientsPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
}

q := fmt.Sprintf(`SELECT c.id, c.name, c.tags, c.identity, c.metadata, COALESCE(c.owner_id, '') AS owner_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)

dbPage, err := toDBClientsPage(pm)
dbPage, err := ToDBClientsPage(pm)
if err != nil {
return clients.ClientsPage{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err)
}
Expand Down Expand Up @@ -190,14 +190,14 @@ func (repo ClientRepository) RetrieveAll(ctx context.Context, pm clients.Page) (
}

func (repo ClientRepository) RetrieveAllBasicInfo(ctx context.Context, pm clients.Page) (clients.ClientsPage, error) {
query, err := pageQuery(pm)
query, err := PageQuery(pm)
if err != nil {
return clients.ClientsPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
}

q := fmt.Sprintf(`SELECT c.id, c.name, c.tags, c.identity FROM clients c %s ORDER BY c.created_at LIMIT :limit OFFSET :offset;`, query)

dbPage, err := toDBClientsPage(pm)
dbPage, err := ToDBClientsPage(pm)
if err != nil {
return clients.ClientsPage{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err)
}
Expand Down Expand Up @@ -246,15 +246,15 @@ func (repo ClientRepository) RetrieveAllByIDs(ctx context.Context, pm clients.Pa
Page: clients.Page{Total: pm.Total, Offset: pm.Offset, Limit: pm.Limit},
}, nil
}
query, err := pageQuery(pm)
query, err := PageQuery(pm)
if err != nil {
return clients.ClientsPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
}

q := fmt.Sprintf(`SELECT c.id, c.name, c.tags, c.identity, c.metadata, COALESCE(c.owner_id, '') AS owner_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)

dbPage, err := toDBClientsPage(pm)
dbPage, err := ToDBClientsPage(pm)
if err != nil {
return clients.ClientsPage{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err)
}
Expand Down Expand Up @@ -334,7 +334,7 @@ type DBClient struct {
UpdatedBy *string `db:"updated_by,omitempty"`
Groups []groups.Group `db:"groups,omitempty"`
Status clients.Status `db:"status,omitempty"`
Role clients.Role `db:"role,omitempty"`
Role *clients.Role `db:"role,omitempty"`
}

func ToDBClient(c clients.Client) (DBClient, error) {
Expand Down Expand Up @@ -375,7 +375,7 @@ func ToDBClient(c clients.Client) (DBClient, error) {
UpdatedAt: updatedAt,
UpdatedBy: updatedBy,
Status: c.Status,
Role: c.Role,
Role: &c.Role,
}, nil
}

Expand Down Expand Up @@ -403,7 +403,7 @@ func ToClient(c DBClient) (clients.Client, error) {
updatedAt = c.UpdatedAt.Time
}

return clients.Client{
cli := clients.Client{
ID: c.ID,
Name: c.Name,
Tags: tags,
Expand All @@ -417,10 +417,14 @@ func ToClient(c DBClient) (clients.Client, error) {
UpdatedAt: updatedAt,
UpdatedBy: updatedBy,
Status: c.Status,
}, nil
}
if c.Role != nil {
cli.Role = *c.Role
}
return cli, nil
}

func toDBClientsPage(pm clients.Page) (dbClientsPage, error) {
func ToDBClientsPage(pm clients.Page) (dbClientsPage, error) {
_, data, err := postgres.CreateMetadataQuery("", pm.Metadata)
if err != nil {
return dbClientsPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
Expand Down Expand Up @@ -457,7 +461,7 @@ type dbClientsPage struct {
Role uint8 `db:"role"`
}

func pageQuery(pm clients.Page) (string, error) {
func PageQuery(pm clients.Page) (string, error) {
mq, _, err := postgres.CreateMetadataQuery("", pm.Metadata)
if err != nil {
return "", errors.Wrap(repoerr.ErrViewEntity, err)
Expand Down
2 changes: 1 addition & 1 deletion things/mocks/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ func (m *Repository) UpdateTags(ctx context.Context, client mgclients.Client) (m
return ret.Get(0).(mgclients.Client), ret.Error(1)
}

func (m *Repository) UpdateRole(ctx context.Context, client mgclients.Client) (mgclients.Client, error) {
func (m *Repository) UpdateOwner(ctx context.Context, client mgclients.Client) (mgclients.Client, error) {
ret := m.Called(ctx, client)

if client.ID == WrongID {
Expand Down
10 changes: 10 additions & 0 deletions users/mocks/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,16 @@ func (m *Repository) UpdateOwner(ctx context.Context, client mgclients.Client) (
return ret.Get(0).(mgclients.Client), ret.Error(1)
}

func (m *Repository) UpdateRole(ctx context.Context, client mgclients.Client) (mgclients.Client, error) {
ret := m.Called(ctx, client)

if client.ID == WrongID {
return mgclients.Client{}, errors.ErrNotFound
}

return ret.Get(0).(mgclients.Client), ret.Error(1)
}

func (m *Repository) RetrieveBySecret(ctx context.Context, key string) (mgclients.Client, error) {
ret := m.Called(ctx, key)

Expand Down
111 changes: 111 additions & 0 deletions users/postgres/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ package postgres
import (
"context"
"database/sql"
"fmt"

"github.com/absmach/magistrala/internal/postgres"
"github.com/absmach/magistrala/pkg/clients"
mgclients "github.com/absmach/magistrala/pkg/clients"
pgclients "github.com/absmach/magistrala/pkg/clients/postgres"
"github.com/absmach/magistrala/pkg/errors"
Expand All @@ -27,6 +29,10 @@ type Repository interface {
// operation failure.
Save(ctx context.Context, client mgclients.Client) (mgclients.Client, error)

RetrieveByID(ctx context.Context, id string) (mgclients.Client, error)

UpdateRole(ctx context.Context, client clients.Client) (clients.Client, error)

CheckSuperAdmin(ctx context.Context, adminID string) error
}

Expand Down Expand Up @@ -85,3 +91,108 @@ func (repo clientRepo) CheckSuperAdmin(ctx context.Context, adminID string) erro
}
return nil
}

func (repo clientRepo) RetrieveByID(ctx context.Context, id string) (clients.Client, error) {
q := `SELECT id, name, tags, COALESCE(owner_id, '') AS owner_id, identity, secret, metadata, created_at, updated_at, updated_by, status, role
FROM clients WHERE id = :id`

dbc := pgclients.DBClient{
ID: id,
}

row, err := repo.DB.NamedQueryContext(ctx, q, dbc)
if err != nil {
if err == sql.ErrNoRows {
return clients.Client{}, errors.Wrap(errors.ErrNotFound, err)
}
return clients.Client{}, errors.Wrap(errors.ErrViewEntity, err)
}

defer row.Close()
row.Next()
dbc = pgclients.DBClient{}
if err := row.StructScan(&dbc); err != nil {
return clients.Client{}, errors.Wrap(errors.ErrNotFound, err)
}

return pgclients.ToClient(dbc)
}

func (repo clientRepo) RetrieveAll(ctx context.Context, pm clients.Page) (clients.ClientsPage, error) {
query, err := pgclients.PageQuery(pm)
if err != nil {
return clients.ClientsPage{}, errors.Wrap(errors.ErrViewEntity, err)
}

q := fmt.Sprintf(`SELECT c.id, c.name, c.tags, c.identity, c.metadata, COALESCE(c.owner_id, '') AS owner_id, c.status, c.role,
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)

dbPage, err := pgclients.ToDBClientsPage(pm)
if err != nil {
return clients.ClientsPage{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err)
}
rows, err := repo.DB.NamedQueryContext(ctx, q, dbPage)
if err != nil {
return clients.ClientsPage{}, errors.Wrap(postgres.ErrFailedToRetrieveAll, err)
}
defer rows.Close()

var items []clients.Client
for rows.Next() {
dbc := pgclients.DBClient{}
if err := rows.StructScan(&dbc); err != nil {
return clients.ClientsPage{}, errors.Wrap(errors.ErrViewEntity, err)
}

c, err := pgclients.ToClient(dbc)
if err != nil {
return clients.ClientsPage{}, err
}

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

total, err := postgres.Total(ctx, repo.DB, cq, dbPage)
if err != nil {
return clients.ClientsPage{}, errors.Wrap(errors.ErrViewEntity, err)
}

page := clients.ClientsPage{
Clients: items,
Page: clients.Page{
Total: total,
Offset: pm.Offset,
Limit: pm.Limit,
},
}

return page, nil
}

func (repo clientRepo) UpdateRole(ctx context.Context, client clients.Client) (clients.Client, error) {
query := `UPDATE clients SET role = :role, updated_at = :updated_at, updated_by = :updated_by
WHERE id = :id AND status = :status
RETURNING id, name, tags, identity, metadata, COALESCE(owner_id, '') AS owner_id, status, role, created_at, updated_at, updated_by`

dbc, err := pgclients.ToDBClient(client)
if err != nil {
return clients.Client{}, errors.Wrap(errors.ErrUpdateEntity, err)
}

row, err := repo.DB.NamedQueryContext(ctx, query, dbc)
if err != nil {
return clients.Client{}, postgres.HandleError(err, errors.ErrUpdateEntity)
}

defer row.Close()
if ok := row.Next(); !ok {
return clients.Client{}, errors.Wrap(errors.ErrNotFound, row.Err())
}
dbc = pgclients.DBClient{}
if err := row.StructScan(&dbc); err != nil {
return clients.Client{}, err
}

return pgclients.ToClient(dbc)
}
2 changes: 1 addition & 1 deletion users/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ func (svc service) UpdateClientRole(ctx context.Context, token string, cli mgcli
if err := svc.updateClientPolicy(ctx, cli.ID, cli.Role); err != nil {
return mgclients.Client{}, errors.Wrap(ErrFailedPolicyUpdate, err)
}
client, err = svc.clients.UpdateOwner(ctx, client)
client, err = svc.clients.UpdateRole(ctx, client)
if err != nil {
// If failed to update role in DB, then revert back to platform admin policy in spicedb
if errRollback := svc.updateClientPolicy(ctx, cli.ID, mgclients.UserRole); errRollback != nil {
Expand Down

0 comments on commit dfcfe45

Please sign in to comment.