Delete lists and add users via email #22

Merged
robin merged 8 commits from dev into main 2026-08-21 21:51:53 +02:00
5 changed files with 43 additions and 11 deletions
Showing only changes of commit 3c5c8a7fe7 - Show all commits
+21 -4
View File
@@ -11,6 +11,7 @@ import (
type ListHandler struct { type ListHandler struct {
ListService *domain.ListService ListService *domain.ListService
UserService *domain.UserService
} }
func NewListHandler(listService *domain.ListService) *ListHandler { func NewListHandler(listService *domain.ListService) *ListHandler {
@@ -99,6 +100,7 @@ func (h *ListHandler) GetLists(w http.ResponseWriter, r *http.Request) {
// @Param payload body request.AddUserToListPayload true "Add user to list payload" // @Param payload body request.AddUserToListPayload true "Add user to list payload"
// @Success 204 {object} nil // @Success 204 {object} nil
// @Error 400 {object} response.ErrorResponse "failed to decode request body" // @Error 400 {object} response.ErrorResponse "failed to decode request body"
// @Error 500 {object} response.ErrorResponse "failed to find email in system"
// @Error 500 {object} response.ErrorResponse "failed to add user to list" // @Error 500 {object} response.ErrorResponse "failed to add user to list"
// @Router /lists/user [post] // @Router /lists/user [post]
func (h *ListHandler) AddUserToList(w http.ResponseWriter, r *http.Request) { func (h *ListHandler) AddUserToList(w http.ResponseWriter, r *http.Request) {
@@ -118,14 +120,21 @@ func (h *ListHandler) AddUserToList(w http.ResponseWriter, r *http.Request) {
return return
} }
err = h.ListService.AddUserToList(ctx, authContext.UserID, payload.ListID, payload.UserID) user, err := h.UserService.GetUserByEmail(ctx, payload.Email)
if err != nil {
slog.ErrorContext(ctx, "failed to get user by email", slog.Any("error", err))
response.Error(ctx, w, http.StatusInternalServerError, "failed to find email in system")
return
}
err = h.ListService.AddUserToList(ctx, authContext.UserID, payload.ListID, user.ID)
if err != nil { if err != nil {
slog.ErrorContext(ctx, "failed to add user to list", slog.Any("error", err)) slog.ErrorContext(ctx, "failed to add user to list", slog.Any("error", err))
response.Error(ctx, w, http.StatusInternalServerError, "failed to add user to list") response.Error(ctx, w, http.StatusInternalServerError, "failed to add user to list")
return return
} }
slog.InfoContext(ctx, "added user to list successfully", slog.Any("list_id", payload.ListID), slog.Any("user_id", payload.UserID)) slog.InfoContext(ctx, "added user to list successfully", slog.Any("list_id", payload.ListID), slog.Any("email", user.Email))
response.Status(ctx, w, http.StatusNoContent) response.Status(ctx, w, http.StatusNoContent)
} }
@@ -139,6 +148,7 @@ func (h *ListHandler) AddUserToList(w http.ResponseWriter, r *http.Request) {
// @Param payload body request.RemoveUserFromListPayload true "Remove user from list payload" // @Param payload body request.RemoveUserFromListPayload true "Remove user from list payload"
// @Success 204 {object} nil // @Success 204 {object} nil
// @Error 400 {object} response.ErrorResponse "failed to decode request body" // @Error 400 {object} response.ErrorResponse "failed to decode request body"
// @Error 500 {object} response.ErrorResponse "failed to find email in system"
// @Error 500 {object} response.ErrorResponse "failed to remove user from list" // @Error 500 {object} response.ErrorResponse "failed to remove user from list"
// @Router /lists/user [delete] // @Router /lists/user [delete]
func (h *ListHandler) RemoveUserFromList(w http.ResponseWriter, r *http.Request) { func (h *ListHandler) RemoveUserFromList(w http.ResponseWriter, r *http.Request) {
@@ -158,14 +168,21 @@ func (h *ListHandler) RemoveUserFromList(w http.ResponseWriter, r *http.Request)
return return
} }
err = h.ListService.RemoveUserFromList(ctx, authContext.UserID, payload.ListID, payload.UserID) user, err := h.UserService.GetUserByEmail(ctx, payload.Email)
if err != nil {
slog.ErrorContext(ctx, "failed to get user by email", slog.Any("error", err))
response.Error(ctx, w, http.StatusInternalServerError, "failed to find email in system")
return
}
err = h.ListService.RemoveUserFromList(ctx, authContext.UserID, payload.ListID, user.ID)
if err != nil { if err != nil {
slog.ErrorContext(ctx, "failed to remove user from list", slog.Any("error", err)) slog.ErrorContext(ctx, "failed to remove user from list", slog.Any("error", err))
response.Error(ctx, w, http.StatusInternalServerError, "failed to remove user from list") response.Error(ctx, w, http.StatusInternalServerError, "failed to remove user from list")
return return
} }
slog.InfoContext(ctx, "removed user from list successfully", slog.Any("list_id", payload.ListID), slog.Any("user_id", payload.UserID)) slog.InfoContext(ctx, "removed user from list successfully", slog.Any("list_id", payload.ListID), slog.Any("email", user.Email))
response.Status(ctx, w, http.StatusNoContent) response.Status(ctx, w, http.StatusNoContent)
} }
+2 -2
View File
@@ -7,12 +7,12 @@ type CreateListPayload struct {
type AddUserToListPayload struct { type AddUserToListPayload struct {
ListID string `json:"list_id"` ListID string `json:"list_id"`
UserID string `json:"user_id"` Email string `json:"email"`
} }
type RemoveUserFromListPayload struct { type RemoveUserFromListPayload struct {
ListID string `json:"list_id"` ListID string `json:"list_id"`
UserID string `json:"user_id"` Email string `json:"email"`
} }
type CreateListItemPayload struct { type CreateListItemPayload struct {
+1 -5
View File
@@ -78,13 +78,9 @@ func (s *ListService) AddUserToList(ctx context.Context, authUserID string, list
return ErrUserIDEmpty return ErrUserIDEmpty
} }
inList, err := s.repo.IsUserInList(ctx, listID, authUserID) if err := s.userAllowedToAccessList(ctx, authUserID, listID); err != nil {
if err != nil {
return err return err
} }
if !inList {
return ErrUserNotInList
}
return s.repo.AddUserToList(ctx, listID, userID) return s.repo.AddUserToList(ctx, listID, userID)
} }
+5
View File
@@ -17,6 +17,7 @@ type User struct {
type UserRepository interface { type UserRepository interface {
CreateUser(ctx context.Context, email string, name string, passwordHash string) (*User, error) CreateUser(ctx context.Context, email string, name string, passwordHash string) (*User, error)
GetUserByEmail(ctx context.Context, email string) (*User, error)
} }
type UserService struct { type UserService struct {
@@ -45,3 +46,7 @@ func (s *UserService) CreateUser(ctx context.Context, email string, name string,
return s.repo.CreateUser(ctx, email, name, string(hash)) return s.repo.CreateUser(ctx, email, name, string(hash))
} }
func (s *UserService) GetUserByEmail(ctx context.Context, email string) (*User, error) {
return s.repo.GetUserByEmail(ctx, email)
}
+14
View File
@@ -39,3 +39,17 @@ func (r *UserRepo) CreateUser(ctx context.Context, email string, name string, pa
return user, nil return user, nil
} }
func (r *UserRepo) GetUserByEmail(ctx context.Context, email string) (*domain.User, error) {
user := &domain.User{}
err := r.db.QueryRowContext(ctx,
"SELECT id, email, name FROM users WHERE email = $1",
email,
).Scan(&user.ID, &user.Email, &user.Name)
if err != nil {
return nil, fmt.Errorf("failed to get user: %w", err)
}
return user, nil
}