Skip to content

Commit

Permalink
user delete endpoint (#132)
Browse files Browse the repository at this point in the history
* add listUserPets to users routes

* feat: create Delete on user repository

* feat: create user delete endpoint

* WIP

* feat: create user safe delete endpoint

* test: add tests for Delete method in UserUsecase

* fix: change logger call on delete
  • Loading branch information
davidspader committed Jun 5, 2024
1 parent e808314 commit 7ba70d0
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 25 deletions.
34 changes: 33 additions & 1 deletion api/controllers/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ func NewUserController(usecase *usecase.UserUsecase) *UserController {
}
}


func (uc *UserController) Insert(w http.ResponseWriter, r *http.Request) {
var userDto dto.UserInsertDto
err := json.NewDecoder(r.Body).Decode(&userDto)
Expand Down Expand Up @@ -133,3 +132,36 @@ func (uc *UserController) FindByID(w http.ResponseWriter, r *http.Request) {

w.WriteHeader(http.StatusOK)
}

func (uc *UserController) Delete(w http.ResponseWriter, r *http.Request) {
userIDFromTokenStr := r.Header.Get("UserId")
userIDFromToken, err := uniqueEntityId.ParseID(userIDFromTokenStr)
if err != nil {
logger.Error("[#UserController.Delete] Erro ao tentar receber o ID do token -> Erro: %v", err)
http.Error(w, "Erro ao converter a requisição ", http.StatusBadRequest)
return
}

IDStr := chi.URLParam(r, "id")
ID, err := uniqueEntityId.ParseID(IDStr)
if err != nil {
logger.Error("[#UserController.Delete] Erro ao tentar converter o body da requisição -> Erro: %v", err)
http.Error(w, "Erro ao converter a requisição ", http.StatusBadRequest)
return
}

if userIDFromToken != ID {
logger.Error("[#UserController.Delete] Erro ao tentar excluir outro usuário -> Erro: %v", err)
http.Error(w, "Usuário não autorizado a excluir este usuário", http.StatusUnauthorized)
return
}

err = uc.usecase.Delete(ID)
if err != nil {
logger.Error("[#UserController.Delete] Erro ao tentar deletar o usuário -> Erro: %v", err)
http.Error(w, "Erro ao tentar atualizar o usuário ", http.StatusBadRequest)
return
}

w.WriteHeader(http.StatusNoContent)
}
1 change: 1 addition & 0 deletions api/middlewares/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ func AuthMiddleware(next http.Handler) http.Handler {
w.WriteHeader(401)
return
}
r.Header.Add("userId", userclaims.Id)
next.ServeHTTP(w, r)
})
}
44 changes: 26 additions & 18 deletions api/routes/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package routes

import (
"pet-dex-backend/v2/api/controllers"
"pet-dex-backend/v2/api/middlewares"

"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
Expand All @@ -19,29 +20,36 @@ func InitRoutes(controllers Controllers, c *chi.Mux) {
c.Route("/api", func(r chi.Router) {
r.Use(middleware.AllowContentType("application/json"))

r.Route("/pets", func(r chi.Router) {
r.Route("/breeds", func(r chi.Router) {
r.Get("/", controllers.BreedController.List)
r.Get("/{breedID}", controllers.BreedController.FindBreed)
r.Group(func(private chi.Router) {
private.Use(middlewares.AuthMiddleware)

private.Route("/pets", func(r chi.Router) {
r.Route("/breeds", func(r chi.Router) {
r.Get("/", controllers.BreedController.List)
})

r.Patch("/{petID}", controllers.PetController.Update)
r.Post("/", controllers.PetController.CreatePet)
})

r.Get("/breeds", controllers.BreedController.List)
r.Patch("/{petID}", controllers.PetController.Update)
r.Post("/", controllers.PetController.CreatePet)
})
private.Route("/ongs", func(r chi.Router) {
r.Post("/", controllers.OngController.Insert)
r.Get("/", controllers.OngController.List)
r.Get("/{ongID}", controllers.OngController.FindByID)
r.Patch("/{ongID}", controllers.OngController.Update)
})

r.Route("/ongs", func(r chi.Router) {
r.Post("/", controllers.OngController.Insert)
r.Get("/", controllers.OngController.List)
r.Get("/{ongID}", controllers.OngController.FindByID)
r.Patch("/{ongID}", controllers.OngController.Update)
private.Route("/user", func(r chi.Router) {
r.Get("/{id}/my-pets", controllers.PetController.ListUserPets)
r.Patch("/{id}", controllers.UserController.Update)
r.Get("/{id}", controllers.UserController.FindByID)
r.Delete("/{id}", controllers.UserController.Delete)
})
})

r.Route("/user", func(r chi.Router) {
r.Post("/token", controllers.UserController.GenerateToken)
r.Post("/", controllers.UserController.Insert)
r.Patch("/{id}", controllers.UserController.Update)
r.Get("/{id}", controllers.UserController.FindByID)
r.Group(func(public chi.Router) {
public.Post("/user", controllers.UserController.Insert)
public.Post("/user/token", controllers.UserController.GenerateToken)
})
})
}
22 changes: 20 additions & 2 deletions infra/db/user_repository.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package db

import (
"database/sql"
"fmt"
"pet-dex-backend/v2/entity"
"pet-dex-backend/v2/infra/config"
Expand All @@ -24,6 +25,13 @@ func NewUserRepository(dbconn *sqlx.DB) interfaces.UserRepository {
}

func (ur *UserRepository) Delete(id uniqueEntityId.ID) error {
_, err := ur.dbconnection.Exec("UPDATE users SET deleted_at = CURRENT_TIMESTAMP WHERE id = ?", id)

if err != nil {
ur.logger.Error("#UserRepository.Delete error: %w", err)
return fmt.Errorf("error on delete user")
}

return nil
}

Expand Down Expand Up @@ -156,8 +164,18 @@ func (ur *UserRepository) FindByID(ID uniqueEntityId.ID) (*entity.User, error) {
return &user, nil
}

func (ur *UserRepository) FindByEmail(email string) *entity.User {
return &entity.User{}
func (ur *UserRepository) FindByEmail(email string) (*entity.User, error) {
var user entity.User
err := ur.dbconnection.QueryRow("SELECT name, pass, email FROM users WHERE email = ?", email).Scan(&user.Name, &user.Pass, &user.Email)

if err != nil {
if err == sql.ErrNoRows {
return nil, nil
}
return nil, fmt.Errorf("error retrieving user: %w", err)
}

return &user, nil
}

func (ur *UserRepository) List() (users []entity.User, err error) {
Expand Down
2 changes: 1 addition & 1 deletion interfaces/user_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type UserRepository interface {
Update(userID uniqueEntityId.ID, user entity.User) error
Delete(id uniqueEntityId.ID) error
FindByID(ID uniqueEntityId.ID) (*entity.User, error)
FindByEmail(email string) *entity.User
FindByEmail(email string) (*entity.User, error)
List() ([]entity.User, error)
AdressRepo
}
4 changes: 2 additions & 2 deletions mocks/pet-dex-backend/v2/interfaces/mock_UserRepository.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 17 additions & 1 deletion usecase/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package usecase

import (
"errors"
"fmt"
"pet-dex-backend/v2/entity"
"pet-dex-backend/v2/entity/dto"
"pet-dex-backend/v2/infra/config"
Expand Down Expand Up @@ -56,7 +57,11 @@ func (uc *UserUsecase) Save(userDto dto.UserInsertDto) error {
}

func (uc *UserUsecase) GenerateToken(loginDto *dto.UserLoginDto) (string, error) {
user := uc.repo.FindByEmail(loginDto.Email)
user, err := uc.repo.FindByEmail(loginDto.Email)
if err != nil {
return "", errors.New("invalid credentials")
}

if user.Name == "" {
return "", errors.New("invalid credentials")
}
Expand Down Expand Up @@ -106,3 +111,14 @@ func (uc *UserUsecase) FindByID(ID uniqueEntityId.ID) (*entity.User, error) {

return user, nil
}

func (uc *UserUsecase) Delete(userID uniqueEntityId.ID) error {
err := uc.repo.Delete(userID)

if err != nil {
uc.logger.Error(fmt.Errorf("#UserUsecase.Delete error: %w", err))
return err
}

return nil
}
50 changes: 50 additions & 0 deletions usecase/user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,3 +259,53 @@ func TestErrorUpdate(t *testing.T) {
})
}
}

func TestDelete(t *testing.T) {
tcases := map[string]struct {
repo *mockInterfaces.MockUserRepository
inputID uniqueEntityId.ID
expectOutput error
}{
"success": {
repo: mockInterfaces.NewMockUserRepository(t),
inputID: uniqueEntityId.NewID(),
expectOutput: nil,
},
}

for name, tcase := range tcases {
t.Run(name, func(t *testing.T) {
tcase.repo.On("Delete", tcase.inputID).Return(tcase.expectOutput)

usecase := NewUserUsecase(tcase.repo, nil, nil)
err := usecase.Delete(tcase.inputID)

assert.Equal(t, tcase.expectOutput, err, "expected error mismatch")
})
}
}

func TestErrorDelete(t *testing.T) {
tcases := map[string]struct {
repo *mockInterfaces.MockUserRepository
inputID uniqueEntityId.ID
expectOutput error
}{
"errorDelete": {
repo: mockInterfaces.NewMockUserRepository(t),
inputID: uniqueEntityId.NewID(),
expectOutput: fmt.Errorf("error on delete user"),
},
}

for name, tcase := range tcases {
t.Run(name, func(t *testing.T) {
tcase.repo.On("Delete", tcase.inputID).Return(tcase.expectOutput)

usecase := NewUserUsecase(tcase.repo, nil, nil)
err := usecase.Delete(tcase.inputID)

assert.Equal(t, tcase.expectOutput, err, "expected error mismatch")
})
}
}

0 comments on commit 7ba70d0

Please sign in to comment.