mirror of
https://github.com/absmach/supermq.git
synced 2026-06-23 06:20:18 +00:00
MG-2287 - Improve users search (#2288)
Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>
This commit is contained in:
@@ -159,7 +159,8 @@ func EncodeError(_ context.Context, err error, w http.ResponseWriter) {
|
||||
errors.Contains(err, apiutil.ErrInvalidDirection),
|
||||
errors.Contains(err, apiutil.ErrInvalidEntityType),
|
||||
errors.Contains(err, apiutil.ErrMissingEntityType),
|
||||
errors.Contains(err, apiutil.ErrInvalidTimeFormat):
|
||||
errors.Contains(err, apiutil.ErrInvalidTimeFormat),
|
||||
errors.Contains(err, svcerr.ErrSearch):
|
||||
err = unwrap(err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
|
||||
|
||||
@@ -78,8 +78,8 @@ type Repository interface {
|
||||
// RetrieveAll retrieves all clients.
|
||||
RetrieveAll(ctx context.Context, pm Page) (ClientsPage, error)
|
||||
|
||||
// RetrieveAllBasicInfo list all clients only with basic information.
|
||||
RetrieveAllBasicInfo(ctx context.Context, pm Page) (ClientsPage, error)
|
||||
// SearchBasicInfo list all clients only with basic information.
|
||||
SearchBasicInfo(ctx context.Context, pm Page) (ClientsPage, error)
|
||||
|
||||
// RetrieveAllByIDs retrieves for given client IDs .
|
||||
RetrieveAllByIDs(ctx context.Context, pm Page) (ClientsPage, error)
|
||||
|
||||
@@ -9,6 +9,7 @@ type Page struct {
|
||||
Offset uint64 `json:"offset"`
|
||||
Limit uint64 `json:"limit"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Id string `json:"id,omitempty"`
|
||||
Order string `json:"order,omitempty"`
|
||||
Dir string `json:"dir,omitempty"`
|
||||
Metadata Metadata `json:"metadata,omitempty"`
|
||||
|
||||
@@ -190,8 +190,8 @@ func (repo *Repository) RetrieveAll(ctx context.Context, pm clients.Page) (clien
|
||||
return page, nil
|
||||
}
|
||||
|
||||
func (repo *Repository) RetrieveAllBasicInfo(ctx context.Context, pm clients.Page) (clients.ClientsPage, error) {
|
||||
sq, tq := constructSearchQuery(pm)
|
||||
func (repo *Repository) SearchBasicInfo(ctx context.Context, pm clients.Page) (clients.ClientsPage, error) {
|
||||
sq, tq := ConstructSearchQuery(pm)
|
||||
|
||||
q := fmt.Sprintf(`SELECT c.id, c.name, c.created_at, c.updated_at FROM clients c %s LIMIT :limit OFFSET :offset;`, sq)
|
||||
|
||||
@@ -334,6 +334,24 @@ func (repo *Repository) Delete(ctx context.Context, id string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *Repository) CheckSuperAdmin(ctx context.Context, adminID string) error {
|
||||
q := "SELECT 1 FROM clients WHERE id = $1 AND role = $2"
|
||||
rows, err := repo.DB.QueryContext(ctx, q, adminID, clients.AdminRole)
|
||||
if err != nil {
|
||||
return postgres.HandleError(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if rows.Next() {
|
||||
if err := rows.Err(); err != nil {
|
||||
return postgres.HandleError(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return repoerr.ErrNotFound
|
||||
}
|
||||
|
||||
type DBClient struct {
|
||||
ID string `db:"id"`
|
||||
Name string `db:"name,omitempty"`
|
||||
@@ -437,6 +455,7 @@ func ToDBClientsPage(pm clients.Page) (dbClientsPage, error) {
|
||||
return dbClientsPage{
|
||||
Name: pm.Name,
|
||||
Identity: pm.Identity,
|
||||
Id: pm.Id,
|
||||
Metadata: data,
|
||||
Domain: pm.Domain,
|
||||
Total: pm.Total,
|
||||
@@ -453,6 +472,7 @@ type dbClientsPage struct {
|
||||
Limit uint64 `db:"limit"`
|
||||
Offset uint64 `db:"offset"`
|
||||
Name string `db:"name"`
|
||||
Id string `db:"id"`
|
||||
Domain string `db:"domain_id"`
|
||||
Identity string `db:"identity"`
|
||||
Metadata []byte `db:"metadata"`
|
||||
@@ -475,11 +495,14 @@ func PageQuery(pm clients.Page) (string, error) {
|
||||
if len(pm.IDs) != 0 {
|
||||
query = append(query, fmt.Sprintf("id IN ('%s')", strings.Join(pm.IDs, "','")))
|
||||
}
|
||||
if pm.Identity != "" {
|
||||
query = append(query, "c.identity = :identity")
|
||||
}
|
||||
if pm.Name != "" {
|
||||
query = append(query, "c.name = :name")
|
||||
query = append(query, "name ILIKE '%' || :name || '%'")
|
||||
}
|
||||
if pm.Identity != "" {
|
||||
query = append(query, "identity ILIKE '%' || :identity || '%'")
|
||||
}
|
||||
if pm.Id != "" {
|
||||
query = append(query, "id ILIKE '%' || :id || '%'")
|
||||
}
|
||||
if pm.Tag != "" {
|
||||
query = append(query, ":tag = ANY(c.tags)")
|
||||
@@ -500,16 +523,19 @@ func PageQuery(pm clients.Page) (string, error) {
|
||||
return emq, nil
|
||||
}
|
||||
|
||||
func constructSearchQuery(pm clients.Page) (string, string) {
|
||||
func ConstructSearchQuery(pm clients.Page) (string, string) {
|
||||
var query []string
|
||||
var emq string
|
||||
var tq string
|
||||
|
||||
if pm.Name != "" {
|
||||
query = append(query, "name ~ :name")
|
||||
query = append(query, "name ILIKE '%' || :name || '%'")
|
||||
}
|
||||
if pm.Identity != "" {
|
||||
query = append(query, "identity ~ :identity")
|
||||
query = append(query, "identity ILIKE '%' || :identity || '%'")
|
||||
}
|
||||
if pm.Id != "" {
|
||||
query = append(query, "id ILIKE '%' || :id || '%'")
|
||||
}
|
||||
|
||||
if len(query) > 0 {
|
||||
|
||||
@@ -927,7 +927,7 @@ func TestRetrieveByIDs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRetrieveAllBasicInfo(t *testing.T) {
|
||||
func TestSearchBasicInfo(t *testing.T) {
|
||||
t.Cleanup(func() {
|
||||
_, err := db.Exec("DELETE FROM clients")
|
||||
require.Nil(t, err, fmt.Sprintf("clean clients unexpected error: %s", err))
|
||||
@@ -1289,7 +1289,7 @@ func TestRetrieveAllBasicInfo(t *testing.T) {
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.desc, func(t *testing.T) {
|
||||
switch response, err := repo.RetrieveAllBasicInfo(context.Background(), c.page); {
|
||||
switch response, err := repo.SearchBasicInfo(context.Background(), c.page); {
|
||||
case err == nil:
|
||||
if c.page.Order != "" && c.page.Dir != "" {
|
||||
c.response = response
|
||||
|
||||
@@ -60,4 +60,7 @@ var (
|
||||
|
||||
// ErrDeletePolicies indicates error in removing policies.
|
||||
ErrDeletePolicies = errors.New("failed to remove policies")
|
||||
|
||||
// ErrSearch indicates error in searching clients.
|
||||
ErrSearch = errors.New("failed to search clients")
|
||||
)
|
||||
|
||||
@@ -1316,6 +1316,9 @@ func (pm PageMetadata) query() (string, error) {
|
||||
if pm.Name != "" {
|
||||
q.Add("name", pm.Name)
|
||||
}
|
||||
if pm.ID != "" {
|
||||
q.Add("id", pm.ID)
|
||||
}
|
||||
if pm.Type != "" {
|
||||
q.Add("type", pm.Type)
|
||||
}
|
||||
|
||||
+28
-28
@@ -91,34 +91,6 @@ func (_m *Repository) RetrieveAll(ctx context.Context, pm clients.Page) (clients
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// RetrieveAllBasicInfo provides a mock function with given fields: ctx, pm
|
||||
func (_m *Repository) RetrieveAllBasicInfo(ctx context.Context, pm clients.Page) (clients.ClientsPage, error) {
|
||||
ret := _m.Called(ctx, pm)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for RetrieveAllBasicInfo")
|
||||
}
|
||||
|
||||
var r0 clients.ClientsPage
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Page) (clients.ClientsPage, error)); ok {
|
||||
return rf(ctx, pm)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Page) clients.ClientsPage); ok {
|
||||
r0 = rf(ctx, pm)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.ClientsPage)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, clients.Page) error); ok {
|
||||
r1 = rf(ctx, pm)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// RetrieveAllByIDs provides a mock function with given fields: ctx, pm
|
||||
func (_m *Repository) RetrieveAllByIDs(ctx context.Context, pm clients.Page) (clients.ClientsPage, error) {
|
||||
ret := _m.Called(ctx, pm)
|
||||
@@ -268,6 +240,34 @@ func (_m *Repository) Save(ctx context.Context, client ...clients.Client) ([]cli
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// SearchBasicInfo provides a mock function with given fields: ctx, pm
|
||||
func (_m *Repository) SearchBasicInfo(ctx context.Context, pm clients.Page) (clients.ClientsPage, error) {
|
||||
ret := _m.Called(ctx, pm)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for SearchBasicInfo")
|
||||
}
|
||||
|
||||
var r0 clients.ClientsPage
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Page) (clients.ClientsPage, error)); ok {
|
||||
return rf(ctx, pm)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Page) clients.ClientsPage); ok {
|
||||
r0 = rf(ctx, pm)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.ClientsPage)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, clients.Page) error); ok {
|
||||
r1 = rf(ctx, pm)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Update provides a mock function with given fields: ctx, client
|
||||
func (_m *Repository) Update(ctx context.Context, client clients.Client) (clients.Client, error) {
|
||||
ret := _m.Called(ctx, client)
|
||||
|
||||
@@ -246,6 +246,10 @@ func decodeListClients(_ context.Context, r *http.Request) (interface{}, error)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
id, err := apiutil.ReadStringQuery(r, api.IDOrder, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
|
||||
st, err := mgclients.ToStatus(s)
|
||||
if err != nil {
|
||||
@@ -262,6 +266,7 @@ func decodeListClients(_ context.Context, r *http.Request) (interface{}, error)
|
||||
tag: t,
|
||||
order: order,
|
||||
dir: dir,
|
||||
id: id,
|
||||
}
|
||||
|
||||
return req, nil
|
||||
|
||||
@@ -81,6 +81,7 @@ func listClientsEndpoint(svc users.Service) endpoint.Endpoint {
|
||||
Identity: req.identity,
|
||||
Order: req.order,
|
||||
Dir: req.dir,
|
||||
Id: req.id,
|
||||
}
|
||||
page, err := svc.ListClients(ctx, req.token, pm)
|
||||
if err != nil {
|
||||
|
||||
@@ -381,6 +381,27 @@ func (lm *loggingMiddleware) ListMembers(ctx context.Context, token, objectKind,
|
||||
return lm.svc.ListMembers(ctx, token, objectKind, objectID, cp)
|
||||
}
|
||||
|
||||
// SearchClients logs the search_clients request. It logs the page metadata and the time it took to complete the request.
|
||||
func (lm *loggingMiddleware) SearchUsers(ctx context.Context, token string, cp mgclients.Page) (mp mgclients.ClientsPage, err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
slog.Group("page",
|
||||
slog.Uint64("limit", cp.Limit),
|
||||
slog.Uint64("offset", cp.Offset),
|
||||
slog.Uint64("total", mp.Total),
|
||||
),
|
||||
}
|
||||
if err != nil {
|
||||
args = append(args, slog.Any("error", err))
|
||||
lm.logger.Warn("Search clients failed to complete successfully", args...)
|
||||
return
|
||||
}
|
||||
lm.logger.Info("Search clients completed successfully", args...)
|
||||
}(time.Now())
|
||||
return lm.svc.SearchUsers(ctx, token, cp)
|
||||
}
|
||||
|
||||
// Identify logs the identify request. It logs the time it took to complete the request.
|
||||
func (lm *loggingMiddleware) Identify(ctx context.Context, token string) (id string, err error) {
|
||||
defer func(begin time.Time) {
|
||||
|
||||
@@ -183,6 +183,15 @@ func (ms *metricsMiddleware) ListMembers(ctx context.Context, token, objectKind,
|
||||
return ms.svc.ListMembers(ctx, token, objectKind, objectID, pm)
|
||||
}
|
||||
|
||||
// SearchClients instruments SearchClients method with metrics.
|
||||
func (ms *metricsMiddleware) SearchUsers(ctx context.Context, token string, pm mgclients.Page) (mp mgclients.ClientsPage, err error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "search_clients").Add(1)
|
||||
ms.latency.With("method", "search_clients").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
return ms.svc.SearchUsers(ctx, token, pm)
|
||||
}
|
||||
|
||||
// Identify instruments Identify method with metrics.
|
||||
func (ms *metricsMiddleware) Identify(ctx context.Context, token string) (string, error) {
|
||||
defer func(begin time.Time) {
|
||||
|
||||
@@ -72,6 +72,7 @@ type listClientsReq struct {
|
||||
metadata mgclients.Metadata
|
||||
order string
|
||||
dir string
|
||||
id string
|
||||
}
|
||||
|
||||
func (req listClientsReq) validate() error {
|
||||
|
||||
@@ -18,6 +18,7 @@ const (
|
||||
valid = "valid"
|
||||
invalid = "invalid"
|
||||
secret = "QJg58*aMan7j"
|
||||
name = "client"
|
||||
)
|
||||
|
||||
var validID = testsutil.GenerateUUID(&testing.T{})
|
||||
|
||||
@@ -31,6 +31,9 @@ type Service interface {
|
||||
// ListMembers retrieves everything that is assigned to a group/thing identified by objectID.
|
||||
ListMembers(ctx context.Context, token, objectKind, objectID string, pm clients.Page) (clients.MembersPage, error)
|
||||
|
||||
// SearchClients searches for users with provided filters for a valid auth token.
|
||||
SearchUsers(ctx context.Context, token string, pm clients.Page) (clients.ClientsPage, error)
|
||||
|
||||
// UpdateClient updates the client's name and metadata.
|
||||
UpdateClient(ctx context.Context, token string, client clients.Client) (clients.Client, error)
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ const (
|
||||
profileView = clientPrefix + "view_profile"
|
||||
clientList = clientPrefix + "list"
|
||||
clientListByGroup = clientPrefix + "list_by_group"
|
||||
clientSearch = clientPrefix + "search"
|
||||
clientIdentify = clientPrefix + "identify"
|
||||
generateResetToken = clientPrefix + "generate_reset_token"
|
||||
issueToken = clientPrefix + "issue_token"
|
||||
@@ -307,6 +308,27 @@ func (lcge listClientByGroupEvent) Encode() (map[string]interface{}, error) {
|
||||
return val, nil
|
||||
}
|
||||
|
||||
type searchClientEvent struct {
|
||||
mgclients.Page
|
||||
}
|
||||
|
||||
func (sce searchClientEvent) Encode() (map[string]interface{}, error) {
|
||||
val := map[string]interface{}{
|
||||
"operation": clientSearch,
|
||||
"total": sce.Total,
|
||||
"offset": sce.Offset,
|
||||
"limit": sce.Limit,
|
||||
}
|
||||
if sce.Name != "" {
|
||||
val["name"] = sce.Name
|
||||
}
|
||||
if sce.Identity != "" {
|
||||
val["identity"] = sce.Identity
|
||||
}
|
||||
|
||||
return val, nil
|
||||
}
|
||||
|
||||
type identifyClientEvent struct {
|
||||
userID string
|
||||
}
|
||||
|
||||
@@ -176,6 +176,22 @@ func (es *eventStore) ListMembers(ctx context.Context, token, objectKind, object
|
||||
return mp, nil
|
||||
}
|
||||
|
||||
func (es *eventStore) SearchUsers(ctx context.Context, token string, pm mgclients.Page) (mgclients.ClientsPage, error) {
|
||||
cp, err := es.svc.SearchUsers(ctx, token, pm)
|
||||
if err != nil {
|
||||
return cp, err
|
||||
}
|
||||
event := searchClientEvent{
|
||||
pm,
|
||||
}
|
||||
|
||||
if err := es.Publish(ctx, event); err != nil {
|
||||
return cp, err
|
||||
}
|
||||
|
||||
return cp, nil
|
||||
}
|
||||
|
||||
func (es *eventStore) EnableClient(ctx context.Context, token, id string) (mgclients.Client, error) {
|
||||
user, err := es.svc.EnableClient(ctx, token, id)
|
||||
if err != nil {
|
||||
|
||||
+28
-28
@@ -109,34 +109,6 @@ func (_m *Repository) RetrieveAll(ctx context.Context, pm clients.Page) (clients
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// RetrieveAllBasicInfo provides a mock function with given fields: ctx, pm
|
||||
func (_m *Repository) RetrieveAllBasicInfo(ctx context.Context, pm clients.Page) (clients.ClientsPage, error) {
|
||||
ret := _m.Called(ctx, pm)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for RetrieveAllBasicInfo")
|
||||
}
|
||||
|
||||
var r0 clients.ClientsPage
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Page) (clients.ClientsPage, error)); ok {
|
||||
return rf(ctx, pm)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Page) clients.ClientsPage); ok {
|
||||
r0 = rf(ctx, pm)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.ClientsPage)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, clients.Page) error); ok {
|
||||
r1 = rf(ctx, pm)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// RetrieveAllByIDs provides a mock function with given fields: ctx, pm
|
||||
func (_m *Repository) RetrieveAllByIDs(ctx context.Context, pm clients.Page) (clients.ClientsPage, error) {
|
||||
ret := _m.Called(ctx, pm)
|
||||
@@ -249,6 +221,34 @@ func (_m *Repository) Save(ctx context.Context, client clients.Client) (clients.
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// SearchBasicInfo provides a mock function with given fields: ctx, pm
|
||||
func (_m *Repository) SearchBasicInfo(ctx context.Context, pm clients.Page) (clients.ClientsPage, error) {
|
||||
ret := _m.Called(ctx, pm)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for SearchBasicInfo")
|
||||
}
|
||||
|
||||
var r0 clients.ClientsPage
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Page) (clients.ClientsPage, error)); ok {
|
||||
return rf(ctx, pm)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, clients.Page) clients.ClientsPage); ok {
|
||||
r0 = rf(ctx, pm)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.ClientsPage)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, clients.Page) error); ok {
|
||||
r1 = rf(ctx, pm)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Update provides a mock function with given fields: ctx, client
|
||||
func (_m *Repository) Update(ctx context.Context, client clients.Client) (clients.Client, error) {
|
||||
ret := _m.Called(ctx, client)
|
||||
|
||||
@@ -331,6 +331,34 @@ func (_m *Service) ResetSecret(ctx context.Context, resetToken string, secret st
|
||||
return r0
|
||||
}
|
||||
|
||||
// SearchUsers provides a mock function with given fields: ctx, token, pm
|
||||
func (_m *Service) SearchUsers(ctx context.Context, token string, pm clients.Page) (clients.ClientsPage, error) {
|
||||
ret := _m.Called(ctx, token, pm)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for SearchUsers")
|
||||
}
|
||||
|
||||
var r0 clients.ClientsPage
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, clients.Page) (clients.ClientsPage, error)); ok {
|
||||
return rf(ctx, token, pm)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, clients.Page) clients.ClientsPage); ok {
|
||||
r0 = rf(ctx, token, pm)
|
||||
} else {
|
||||
r0 = ret.Get(0).(clients.ClientsPage)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, clients.Page) error); ok {
|
||||
r1 = rf(ctx, token, pm)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// SendPasswordReset provides a mock function with given fields: ctx, host, email, user, token
|
||||
func (_m *Service) SendPasswordReset(ctx context.Context, host string, email string, user string, token string) error {
|
||||
ret := _m.Called(ctx, host, email, user, token)
|
||||
|
||||
+15
-2
@@ -196,11 +196,10 @@ func (svc service) ListClients(ctx context.Context, token string, pm mgclients.P
|
||||
Identity: pm.Identity,
|
||||
Role: mgclients.UserRole,
|
||||
}
|
||||
pg, err := svc.clients.RetrieveAll(ctx, p)
|
||||
pg, err := svc.clients.SearchBasicInfo(ctx, p)
|
||||
if err != nil {
|
||||
return mgclients.ClientsPage{}, errors.Wrap(svcerr.ErrViewEntity, err)
|
||||
}
|
||||
|
||||
for i, c := range pg.Clients {
|
||||
pg.Clients[i] = mgclients.Client{ID: c.ID, Name: c.Name}
|
||||
}
|
||||
@@ -554,6 +553,20 @@ func (svc service) ListMembers(ctx context.Context, token, objectKind, objectID
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (svc service) SearchUsers(ctx context.Context, token string, pm mgclients.Page) (mgclients.ClientsPage, error) {
|
||||
_, err := svc.identify(ctx, token)
|
||||
if err != nil {
|
||||
return mgclients.ClientsPage{}, err
|
||||
}
|
||||
|
||||
cp, err := svc.clients.SearchBasicInfo(ctx, pm)
|
||||
if err != nil {
|
||||
return mgclients.ClientsPage{}, errors.Wrap(svcerr.ErrSearch, err)
|
||||
}
|
||||
|
||||
return cp, nil
|
||||
}
|
||||
|
||||
func (svc service) retrieveObjectUsersPermissions(ctx context.Context, domainID, objectType, objectID string, client *mgclients.Client) error {
|
||||
userID := auth.EncodeDomainUserID(domainID, client.ID)
|
||||
permissions, err := svc.listObjectUserPermission(ctx, userID, objectType, objectID)
|
||||
|
||||
+76
-1
@@ -30,7 +30,7 @@ var (
|
||||
phasher = hasher.New()
|
||||
secret = "strongsecret"
|
||||
validCMetadata = mgclients.Metadata{"role": "client"}
|
||||
clientID = testsutil.GenerateUUID(&testing.T{})
|
||||
clientID = "d8dd12ef-aa2a-43fe-8ef2-2e4fe514360f"
|
||||
client = mgclients.Client{
|
||||
ID: clientID,
|
||||
Name: "clientname",
|
||||
@@ -597,6 +597,7 @@ func TestListClients(t *testing.T) {
|
||||
authCall1 := auth.On("Authorize", context.Background(), mock.Anything).Return(tc.authorizeResponse, tc.authorizeErr)
|
||||
repoCall := cRepo.On("CheckSuperAdmin", context.Background(), mock.Anything).Return(tc.superAdminErr)
|
||||
repoCall1 := cRepo.On("RetrieveAll", context.Background(), mock.Anything).Return(tc.retrieveAllResponse, tc.retrieveAllErr)
|
||||
repoCall2 := cRepo.On("SearchBasicInfo", context.Background(), mock.Anything).Return(tc.response, tc.err)
|
||||
page, err := svc.ListClients(context.Background(), tc.token, tc.page)
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
assert.Equal(t, tc.response, page, fmt.Sprintf("%s: expected %v got %v\n", tc.desc, tc.response, page))
|
||||
@@ -608,6 +609,7 @@ func TestListClients(t *testing.T) {
|
||||
authCall1.Unset()
|
||||
repoCall.Unset()
|
||||
repoCall1.Unset()
|
||||
repoCall2.Unset()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2167,6 +2169,79 @@ func TestListMembers(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchUsers(t *testing.T) {
|
||||
svc, cRepo, auth, _ := newService(true)
|
||||
cases := []struct {
|
||||
desc string
|
||||
token string
|
||||
page mgclients.Page
|
||||
identifyResp *magistrala.IdentityRes
|
||||
response mgclients.ClientsPage
|
||||
responseErr error
|
||||
identifyErr error
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "search clients with valid token",
|
||||
token: validToken,
|
||||
page: mgclients.Page{Offset: 0, Name: "clientname", Limit: 100},
|
||||
response: mgclients.ClientsPage{
|
||||
Page: mgclients.Page{Total: 1, Offset: 0, Limit: 100},
|
||||
Clients: []mgclients.Client{client},
|
||||
},
|
||||
identifyResp: &magistrala.IdentityRes{UserId: client.ID},
|
||||
},
|
||||
{
|
||||
desc: "search clients with invalid token",
|
||||
token: inValidToken,
|
||||
page: mgclients.Page{Offset: 0, Name: "clientname", Limit: 100},
|
||||
response: mgclients.ClientsPage{},
|
||||
responseErr: svcerr.ErrAuthentication,
|
||||
err: svcerr.ErrAuthentication,
|
||||
},
|
||||
{
|
||||
desc: "search clients with id",
|
||||
token: validToken,
|
||||
page: mgclients.Page{Offset: 0, Id: "d8dd12ef-aa2a-43fe-8ef2-2e4fe514360f", Limit: 100},
|
||||
response: mgclients.ClientsPage{
|
||||
Page: mgclients.Page{Total: 1, Offset: 0, Limit: 100},
|
||||
Clients: []mgclients.Client{client},
|
||||
},
|
||||
identifyResp: &magistrala.IdentityRes{UserId: client.ID},
|
||||
},
|
||||
{
|
||||
desc: "search clients with username",
|
||||
token: validToken,
|
||||
page: mgclients.Page{Offset: 0, Identity: "clientidentity", Limit: 100},
|
||||
response: mgclients.ClientsPage{
|
||||
Page: mgclients.Page{Total: 1, Offset: 0, Limit: 100},
|
||||
Clients: []mgclients.Client{client},
|
||||
},
|
||||
identifyResp: &magistrala.IdentityRes{UserId: client.ID},
|
||||
},
|
||||
{
|
||||
desc: "search clients with random name",
|
||||
token: validToken,
|
||||
page: mgclients.Page{Offset: 0, Name: "randomname", Limit: 100},
|
||||
response: mgclients.ClientsPage{
|
||||
Page: mgclients.Page{Total: 0, Offset: 0, Limit: 100},
|
||||
Clients: []mgclients.Client{},
|
||||
},
|
||||
identifyResp: &magistrala.IdentityRes{UserId: client.ID},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
authCall := auth.On("Identify", context.Background(), &magistrala.IdentityReq{Token: tc.token}).Return(tc.identifyResp, tc.identifyErr)
|
||||
repoCall := cRepo.On("SearchBasicInfo", context.Background(), tc.page).Return(tc.response, tc.responseErr)
|
||||
page, err := svc.SearchUsers(context.Background(), tc.token, tc.page)
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
assert.Equal(t, tc.response, page, fmt.Sprintf("%s: expected %v got %v\n", tc.desc, tc.response, page))
|
||||
authCall.Unset()
|
||||
repoCall.Unset()
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssueToken(t *testing.T) {
|
||||
svc, cRepo, auth, _ := newService(true)
|
||||
|
||||
|
||||
@@ -185,6 +185,14 @@ func (tm *tracingMiddleware) ListMembers(ctx context.Context, token, objectKind,
|
||||
return tm.svc.ListMembers(ctx, token, objectKind, objectID, pm)
|
||||
}
|
||||
|
||||
// SearchClients traces the "SearchClients" operation of the wrapped clients.Service.
|
||||
func (tm *tracingMiddleware) SearchUsers(ctx context.Context, token string, pm mgclients.Page) (mgclients.ClientsPage, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_search_clients", trace.WithAttributes(attribute.String("token", token)))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.SearchUsers(ctx, token, pm)
|
||||
}
|
||||
|
||||
// Identify traces the "Identify" operation of the wrapped clients.Service.
|
||||
func (tm *tracingMiddleware) Identify(ctx context.Context, token string) (string, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "svc_identify", trace.WithAttributes(attribute.String("token", token)))
|
||||
|
||||
Reference in New Issue
Block a user