Skip to content

Commit

Permalink
feat(admin-api): get users by multiple ids
Browse files Browse the repository at this point in the history
  • Loading branch information
lfleischmann committed Sep 27, 2024
1 parent 579cef3 commit 95aa8da
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 25 deletions.
19 changes: 11 additions & 8 deletions backend/handler/user_admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ type UserListRequest struct {
PerPage int `query:"per_page"`
Page int `query:"page"`
Email string `query:"email"`
UserId string `query:"user_id"`
UserID string `query:"user_id"`
Username string `query:"username"`
SortDirection string `query:"sort_direction"`
}
Expand All @@ -83,11 +83,14 @@ func (h *UserHandlerAdmin) List(c echo.Context) error {
request.PerPage = 20
}

userId := uuid.Nil
if request.UserId != "" {
userId, err = uuid.FromString(request.UserId)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "failed to parse user_id as uuid").SetInternal(err)
var userIDs []uuid.UUID
if request.UserID != "" {
for _, userIDString := range strings.Split(request.UserID, ",") {
userID, err := uuid.FromString(userIDString)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "failed to parse user_id as uuid").SetInternal(err)
}
userIDs = append(userIDs, userID)
}
}

Expand All @@ -104,12 +107,12 @@ func (h *UserHandlerAdmin) List(c echo.Context) error {
email := strings.ToLower(request.Email)
username := strings.ToLower(request.Username)

users, err := h.persister.GetUserPersister().List(request.Page, request.PerPage, userId, email, username, request.SortDirection)
users, err := h.persister.GetUserPersister().List(request.Page, request.PerPage, userIDs, email, username, request.SortDirection)
if err != nil {
return fmt.Errorf("failed to get list of users: %w", err)
}

userCount, err := h.persister.GetUserPersister().Count(userId, email, username)
userCount, err := h.persister.GetUserPersister().Count(userIDs, email, username)
if err != nil {
return fmt.Errorf("failed to get total count of users: %w", err)
}
Expand Down
31 changes: 29 additions & 2 deletions backend/handler/user_admin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func (s *userAdminSuite) TestUserHandlerAdmin_Delete() {

s.Equal(http.StatusNoContent, rec.Code)

count, err := s.Storage.GetUserPersister().Count(uuid.Nil, "", "")
count, err := s.Storage.GetUserPersister().Count([]uuid.UUID{}, "", "")
s.Require().NoError(err)
s.Equal(2, count)
}
Expand All @@ -59,7 +59,7 @@ func (s *userAdminSuite) TestUserHandlerAdmin_Delete_UnknownUserId() {

s.Equal(http.StatusNotFound, rec.Code)

count, err := s.Storage.GetUserPersister().Count(uuid.Nil, "", "")
count, err := s.Storage.GetUserPersister().Count([]uuid.UUID{}, "", "")
s.Require().NoError(err)
s.Equal(3, count)
}
Expand Down Expand Up @@ -144,6 +144,33 @@ func (s *userAdminSuite) TestUserHandlerAdmin_List_NoUsers() {
}
}

func (s *userAdminSuite) TestUserHandlerAdmin_List_MultipleUserIDs() {
if testing.Short() {
s.T().Skip("skipping test in short mode.")
}

err := s.LoadFixtures("../test/fixtures/user_admin")
s.Require().NoError(err)

e := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)

req := httptest.NewRequest(http.MethodGet, "/users?user_id=b5dd5267-b462-48be-b70d-bcd6f1bbe7a5,e0282f3f-b211-4f0e-b777-6fabc69287c9", nil)
rec := httptest.NewRecorder()

e.ServeHTTP(rec, req)

if s.Equal(http.StatusOK, rec.Code) {
s.Equal("2", rec.Header().Get("X-Total-Count"))

var got []models.User
err := json.Unmarshal(rec.Body.Bytes(), &got)
s.Require().NoError(err)

s.Equal(2, len(got))
s.Equal("<http://example.com/users?page=1&per_page=20&user_id=b5dd5267-b462-48be-b70d-bcd6f1bbe7a5%2Ce0282f3f-b211-4f0e-b777-6fabc69287c9>; rel=\"first\"", rec.Header().Get("Link"))
}
}

func (s *userAdminSuite) TestUserHandlerAdmin_List_InvalidPaginationParam() {
e := NewAdminRouter(&test.DefaultConfig, s.Storage, nil)

Expand Down
8 changes: 4 additions & 4 deletions backend/handler/user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func (s *userSuite) TestUserHandler_Create_TokenInCookie() {
s.NoError(err)
s.False(user.UserID.IsNil())

count, err := s.Storage.GetUserPersister().Count(uuid.Nil, "", "")
count, err := s.Storage.GetUserPersister().Count([]uuid.UUID{}, "", "")
s.NoError(err)
s.Equal(1, count)

Expand Down Expand Up @@ -97,7 +97,7 @@ func (s *userSuite) TestUserHandler_Create_TokenInHeader() {
s.NoError(err)
s.False(user.UserID.IsNil())

count, err := s.Storage.GetUserPersister().Count(uuid.Nil, "", "")
count, err := s.Storage.GetUserPersister().Count([]uuid.UUID{}, "", "")
s.NoError(err)
s.Equal(1, count)

Expand Down Expand Up @@ -137,7 +137,7 @@ func (s *userSuite) TestUserHandler_Create_CaseInsensitive() {
s.NoError(err)
s.False(user.UserID.IsNil())

count, err := s.Storage.GetUserPersister().Count(uuid.Nil, "", "")
count, err := s.Storage.GetUserPersister().Count([]uuid.UUID{}, "", "")
s.NoError(err)
s.Equal(1, count)

Expand Down Expand Up @@ -572,7 +572,7 @@ func (s *userSuite) TestUserHandler_Delete() {
s.Equal("Max-Age=0", strings.TrimSpace(split[2]))
}

count, err := s.Storage.GetUserPersister().Count(uuid.Nil, "", "")
count, err := s.Storage.GetUserPersister().Count([]uuid.UUID{}, "", "")
s.NoError(err)
s.Equal(0, count)
}
18 changes: 9 additions & 9 deletions backend/persistence/user_persister.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ type UserPersister interface {
Create(models.User) error
Update(models.User) error
Delete(models.User) error
List(page int, perPage int, userId uuid.UUID, email string, username string, sortDirection string) ([]models.User, error)
List(page int, perPage int, userIDs []uuid.UUID, email string, username string, sortDirection string) ([]models.User, error)
All() ([]models.User, error)
Count(userId uuid.UUID, email string, username string) (int, error)
Count(userIDs []uuid.UUID, email string, username string) (int, error)
GetByUsername(username string) (*models.User, error)
}

Expand Down Expand Up @@ -129,15 +129,15 @@ func (p *userPersister) Delete(user models.User) error {
return nil
}

func (p *userPersister) List(page int, perPage int, userId uuid.UUID, email string, username string, sortDirection string) ([]models.User, error) {
func (p *userPersister) List(page int, perPage int, userIDs []uuid.UUID, email string, username string, sortDirection string) ([]models.User, error) {
users := []models.User{}

query := p.db.
Q().
EagerPreload("Emails", "Emails.PrimaryEmail", "WebauthnCredentials", "Username").
LeftJoin("emails", "emails.user_id = users.id").
LeftJoin("usernames", "usernames.user_id = users.id")
query = p.addQueryParamsToSqlQuery(query, userId, email, username)
query = p.addQueryParamsToSqlQuery(query, userIDs, email, username)
err := query.GroupBy("users.id").
Having("count(emails.id) > 0 OR count(usernames.id) > 0").
Order(fmt.Sprintf("users.created_at %s", sortDirection)).
Expand Down Expand Up @@ -167,12 +167,12 @@ func (p *userPersister) All() ([]models.User, error) {
return users, nil
}

func (p *userPersister) Count(userId uuid.UUID, email string, username string) (int, error) {
func (p *userPersister) Count(userIDs []uuid.UUID, email string, username string) (int, error) {
query := p.db.
Q().
LeftJoin("emails", "emails.user_id = users.id").
LeftJoin("usernames", "usernames.user_id = users.id")
query = p.addQueryParamsToSqlQuery(query, userId, email, username)
query = p.addQueryParamsToSqlQuery(query, userIDs, email, username)
count, err := query.GroupBy("users.id").
Having("count(emails.id) > 0 OR count(usernames.id) > 0").
Count(&models.User{})
Expand All @@ -183,7 +183,7 @@ func (p *userPersister) Count(userId uuid.UUID, email string, username string) (
return count, nil
}

func (p *userPersister) addQueryParamsToSqlQuery(query *pop.Query, userId uuid.UUID, email string, username string) *pop.Query {
func (p *userPersister) addQueryParamsToSqlQuery(query *pop.Query, userIDs []uuid.UUID, email string, username string) *pop.Query {
if email != "" && username != "" {
query = query.Where("emails.address LIKE ? OR usernames.username LIKE ?", "%"+email+"%", "%"+username+"%")
} else if email != "" {
Expand All @@ -192,8 +192,8 @@ func (p *userPersister) addQueryParamsToSqlQuery(query *pop.Query, userId uuid.U
query = query.Where("usernames.username LIKE ?", "%"+username+"%")
}

if !userId.IsNil() {
query = query.Where("users.id = ?", userId)
if len(userIDs) > 0 {
query = query.Where("users.id in (?)", userIDs)
}

return query
Expand Down
4 changes: 2 additions & 2 deletions backend/test/user_persister.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func (p *userPersister) Delete(user models.User) error {
return nil
}

func (p *userPersister) List(page int, perPage int, userId uuid.UUID, email string, username string, sortDirection string) ([]models.User, error) {
func (p *userPersister) List(page int, perPage int, userIDs []uuid.UUID, email string, username string, sortDirection string) ([]models.User, error) {
if len(p.users) == 0 {
return p.users, nil
}
Expand Down Expand Up @@ -96,7 +96,7 @@ func (p *userPersister) All() ([]models.User, error) {
return p.users, nil
}

func (p *userPersister) Count(userId uuid.UUID, email string, username string) (int, error) {
func (p *userPersister) Count(userIDs []uuid.UUID, email string, username string) (int, error) {
return len(p.users), nil
}

Expand Down

0 comments on commit 95aa8da

Please sign in to comment.