Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add update box endpoint #21

Merged
merged 8 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions internal/app/application/services/box.go
Original file line number Diff line number Diff line change
Expand Up @@ -353,3 +353,24 @@ func (s *BoxService) DeleteWithTransactionsAndItemQuantities(boxID string) error

return nil
}

func (s *BoxService) Update(
boxID string,
name string,
description *string,
) (*entities.Box, error) {
box, err := s.boxRepository.GetByID(boxID)
if err != nil {
return nil, err
}

box.Name = name
box.Description = description

err = s.boxRepository.Update(box)
if err != nil {
return nil, err
}

return box, nil
}
88 changes: 88 additions & 0 deletions internal/app/application/services/box_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -738,3 +738,91 @@ func TestBoxServiceDeleteWithTransactionsAndItemQuantitiesErrorInBoxRepositoryOn
itemRepository.AssertExpectations(t)
roomRepository.AssertExpectations(t)
}

func TestBoxServiceUpdate(t *testing.T) {
boxRepository := new(stub.BoxRepositoryMock)
roomRepository := new(stub.RoomRepositoryMock)
itemRepository := new(stub.ItemRepositoryMock)

boxService := NewBoxService(boxRepository, itemRepository, roomRepository)

boxID := uuid.NewString()
name := "box"
description := "description"

boxRepository.On("GetByID", boxID).
Return(&entities.Box{
ID: boxID,
Name: name,
Description: &description,
}, nil)
boxRepository.On("Update", mock.AnythingOfType("*entities.Box")).
Return(nil)

box, err := boxService.Update(boxID, name, &description)

assert.NoError(t, err)
assert.NotNil(t, box)
assert.Equal(t, boxID, box.ID)
assert.Equal(t, name, box.Name)
assert.Equal(t, description, *box.Description)
boxRepository.AssertExpectations(t)
itemRepository.AssertExpectations(t)
roomRepository.AssertExpectations(t)
}

func TestBoxServiceUpdateErrorInBoxRepositoryOnGetByID(t *testing.T) {
boxRepository := new(stub.BoxRepositoryMock)
roomRepository := new(stub.RoomRepositoryMock)
itemRepository := new(stub.ItemRepositoryMock)

boxService := NewBoxService(boxRepository, itemRepository, roomRepository)

boxID := uuid.NewString()
name := "box"
description := "description"

mockError := errors.New("repository error")
boxRepository.On("GetByID", boxID).
Return(nil, mockError)

box, err := boxService.Update(boxID, name, &description)

assert.Error(t, err)
assert.Nil(t, box)
assert.EqualError(t, err, mockError.Error())
boxRepository.AssertExpectations(t)
itemRepository.AssertExpectations(t)
roomRepository.AssertExpectations(t)
}

func TestBoxServiceUpdateErrorInBoxRepositoryOnUpdate(t *testing.T) {
boxRepository := new(stub.BoxRepositoryMock)
roomRepository := new(stub.RoomRepositoryMock)
itemRepository := new(stub.ItemRepositoryMock)

boxService := NewBoxService(boxRepository, itemRepository, roomRepository)

boxID := uuid.NewString()
name := "box"
description := "description"

mockError := errors.New("repository error")
boxRepository.On("GetByID", boxID).
Return(&entities.Box{
ID: boxID,
Name: name,
Description: &description,
}, nil)
boxRepository.On("Update", mock.AnythingOfType("*entities.Box")).
Return(mockError)

box, err := boxService.Update(boxID, name, &description)

assert.Error(t, err)
assert.Nil(t, box)
assert.EqualError(t, err, mockError.Error())
boxRepository.AssertExpectations(t)
itemRepository.AssertExpectations(t)
roomRepository.AssertExpectations(t)
}
8 changes: 6 additions & 2 deletions internal/app/domain/repositories/box.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ var (
ErrBoxRepositoryCanNotDeleteBoxItemsByBoxID = errors.New("can not delete box items by box id")
ErrBoxRepositoryCanNotDeleteBoxTransactionsByBoxID = errors.New("can not delete box transactions by box id")
ErrBoxRepositoryCanNotUpdateBoxItem = errors.New("can not update box item")
ErrorBoxRepositoryCanNotCountByQueryFilters = errors.New("can not count by query filters")
ErrorBoxRepositoryCanNotGetByQueryFilters = errors.New("can not get by query filters")
ErrBoxRepositoryCanNotCountByQueryFilters = errors.New("can not count by query filters")
ErrBoxRepositoryCanNotGetByQueryFilters = errors.New("can not get by query filters")
ErrBoxRepositoryCanNotGetByID = errors.New("can not get by id")
ErrBoxRepositoryCanNotUpdate = errors.New("can not update")
)

type BoxRepository interface {
Expand All @@ -31,4 +33,6 @@ type BoxRepository interface {
DeleteBoxItemsByBoxID(boxID string) error
DeleteBoxTransactionsByBoxID(boxID string) error
Delete(id string) error
GetByID(id string) (*entities.Box, error)
Update(box *entities.Box) error
}
57 changes: 57 additions & 0 deletions internal/app/infrastructure/controllers/update_box.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package controllers

import (
"github.com/jibaru/home-inventory-api/m/internal/app/application/services"
"github.com/jibaru/home-inventory-api/m/internal/app/infrastructure/responses"
"github.com/labstack/echo/v4"
"net/http"
)

type UpdateBoxController struct {
boxService *services.BoxService
}

type UpdateBoxRequest struct {
Name string `json:"name"`
Description *string `json:"description"`
BoxID string `param:"boxID"`
}

type UpdateBoxResponse struct {
ID string `json:"id"`
Name string `json:"name"`
Description *string `json:"description"`
}

func NewUpdateBoxController(boxService *services.BoxService) *UpdateBoxController {
return &UpdateBoxController{
boxService,
}
}

func (c *UpdateBoxController) Handle(ctx echo.Context) error {
request := UpdateBoxRequest{}

err := (&echo.DefaultBinder{}).BindBody(ctx, &request)
if err != nil {
return ctx.JSON(http.StatusBadRequest, responses.NewMessageResponse(err.Error()))
}

err = (&echo.DefaultBinder{}).BindPathParams(ctx, &request)
if err != nil {
return ctx.JSON(http.StatusBadRequest, responses.NewMessageResponse(err.Error()))
}

box, err := c.boxService.Update(request.BoxID, request.Name, request.Description)
if err != nil {
return ctx.JSON(http.StatusBadRequest, responses.NewMessageResponse(err.Error()))
}

return ctx.JSON(http.StatusOK, responses.NewDataResponse(
&UpdateBoxResponse{
ID: box.ID,
Name: box.Name,
Description: box.Description,
},
))
}
2 changes: 2 additions & 0 deletions internal/app/infrastructure/http/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ func RunServer(
deleteBoxController := controllers.NewDeleteBoxController(boxService)
deleteRoomController := controllers.NewDeleteRoomController(roomService)
updateRoomController := controllers.NewUpdateRoomController(roomService)
updateBoxController := controllers.NewUpdateBoxController(boxService)

loggerMiddleware := middlewares.NewLoggerMiddleware()
needsAuthMiddleware := middlewares.NewNeedsAuthMiddleware(authService)
Expand All @@ -85,6 +86,7 @@ func RunServer(
authApi.DELETE("/boxes/:boxID", deleteBoxController.Handle)
authApi.DELETE("/rooms/:roomID", deleteRoomController.Handle)
authApi.PATCH("/rooms/:roomID", updateRoomController.Handle)
authApi.PATCH("/boxes/:boxID", updateBoxController.Handle)

logger.LogError(e.Start(host + ":" + port))
}
24 changes: 22 additions & 2 deletions internal/app/infrastructure/repositories/gorm/box.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func (r *BoxRepository) GetByQueryFilters(queryFilter repositories.QueryFilter,

if err != nil {
logger.LogError(err)
return nil, repositories.ErrorBoxRepositoryCanNotGetByQueryFilters
return nil, repositories.ErrBoxRepositoryCanNotGetByQueryFilters
}

return boxes, nil
Expand All @@ -103,7 +103,7 @@ func (r *BoxRepository) CountByQueryFilters(queryFilter repositories.QueryFilter

if err != nil {
logger.LogError(err)
return 0, repositories.ErrorBoxRepositoryCanNotCountByQueryFilters
return 0, repositories.ErrBoxRepositoryCanNotCountByQueryFilters
}

return count, nil
Expand Down Expand Up @@ -138,3 +138,23 @@ func (r *BoxRepository) Delete(id string) error {

return nil
}

func (r *BoxRepository) GetByID(id string) (*entities.Box, error) {
var box entities.Box
if err := r.db.Where("id = ?", id).First(&box).Error; err != nil {
logger.LogError(err)
return nil, repositories.ErrBoxRepositoryCanNotGetByID
}

return &box, nil
}

func (r *BoxRepository) Update(box *entities.Box) error {
if err := r.db.Save(box).Error; err != nil {
logger.LogError(err)
notifier.NotifyError(err)
return repositories.ErrBoxRepositoryCanNotUpdate
}

return nil
}
104 changes: 103 additions & 1 deletion internal/app/infrastructure/repositories/gorm/box_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ func TestBoxRepositoryGetByQueryFiltersErrorBoxRepositoryCanNotGetByQueryFilters
result, err := boxRepository.GetByQueryFilters(queryFilter, pageFilter)

assert.Error(t, err)
assert.ErrorIs(t, err, repositories.ErrorBoxRepositoryCanNotGetByQueryFilters)
assert.ErrorIs(t, err, repositories.ErrBoxRepositoryCanNotGetByQueryFilters)
assert.Nil(t, result)
err = dbMock.ExpectationsWereMet()
assert.NoError(t, err)
Expand Down Expand Up @@ -642,3 +642,105 @@ func TestBoxRepositoryDeleteErrorBoxRepositoryCanNotDeleteBox(t *testing.T) {
err = dbMock.ExpectationsWereMet()
assert.NoError(t, err)
}

func TestBoxRepositoryGetByID(t *testing.T) {
db, dbMock := makeDBMock()
boxRepository := NewBoxRepository(db)

boxID := uuid.NewString()
description := random.String(255, random.Alphanumeric)
box := &entities.Box{
ID: boxID,
Name: random.String(100, random.Alphanumeric),
Description: &description,
RoomID: uuid.NewString(),
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}

dbMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `boxes` WHERE id = ? ORDER BY `boxes`.`id` LIMIT 1")).
WithArgs(boxID).
WillReturnRows(sqlmock.NewRows([]string{"id", "name", "description", "room_id", "created_at", "updated_at"}).
AddRow(box.ID, box.Name, *box.Description, box.RoomID, box.CreatedAt, box.UpdatedAt))

result, err := boxRepository.GetByID(boxID)

assert.NoError(t, err)
assert.Equal(t, box, result)
err = dbMock.ExpectationsWereMet()
assert.NoError(t, err)
}

func TestBoxRepositoryGetByIDErrorBoxRepositoryCanNotGetByID(t *testing.T) {
db, dbMock := makeDBMock()
boxRepository := NewBoxRepository(db)

boxID := uuid.NewString()

dbMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `boxes` WHERE id = ? ORDER BY `boxes`.`id` LIMIT 1")).
WithArgs(boxID).
WillReturnError(errors.New("record not found"))

result, err := boxRepository.GetByID(boxID)

assert.Error(t, err)
assert.ErrorIs(t, err, repositories.ErrBoxRepositoryCanNotGetByID)
assert.Nil(t, result)
err = dbMock.ExpectationsWereMet()
assert.NoError(t, err)
}

func TestBoxRepositoryUpdate(t *testing.T) {
db, dbMock := makeDBMock()
boxRepository := NewBoxRepository(db)

description := random.String(255, random.Alphanumeric)
box := &entities.Box{
ID: uuid.NewString(),
Name: random.String(100, random.Alphanumeric),
Description: &description,
RoomID: uuid.NewString(),
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}

dbMock.ExpectBegin()
dbMock.ExpectExec(regexp.QuoteMeta("UPDATE `boxes` SET `name`=?,`description`=?,`room_id`=?,`created_at`=?,`updated_at`=? WHERE `id` = ?")).
WithArgs(box.Name, *box.Description, box.RoomID, box.CreatedAt, sqlmock.AnyArg(), box.ID).
WillReturnResult(sqlmock.NewResult(1, 1))
dbMock.ExpectCommit()

err := boxRepository.Update(box)

assert.NoError(t, err)
err = dbMock.ExpectationsWereMet()
assert.NoError(t, err)
}

func TestBoxRepositoryUpdateErrorBoxRepositoryCanNotUpdate(t *testing.T) {
db, dbMock := makeDBMock()
boxRepository := NewBoxRepository(db)

description := random.String(255, random.Alphanumeric)
box := &entities.Box{
ID: uuid.NewString(),
Name: random.String(100, random.Alphanumeric),
Description: &description,
RoomID: uuid.NewString(),
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}

dbMock.ExpectBegin()
dbMock.ExpectExec(regexp.QuoteMeta("UPDATE `boxes` SET `name`=?,`description`=?,`room_id`=?,`created_at`=?,`updated_at`=? WHERE `id` = ?")).
WithArgs(box.Name, *box.Description, box.RoomID, box.CreatedAt, sqlmock.AnyArg(), box.ID).
WillReturnError(errors.New("database error"))
dbMock.ExpectRollback()

err := boxRepository.Update(box)

assert.Error(t, err)
assert.ErrorIs(t, err, repositories.ErrBoxRepositoryCanNotUpdate)
err = dbMock.ExpectationsWereMet()
assert.NoError(t, err)
}
15 changes: 15 additions & 0 deletions internal/app/infrastructure/repositories/stub/box.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,18 @@ func (m *BoxRepositoryMock) Delete(id string) error {
args := m.Called(id)
return args.Error(0)
}

func (m *BoxRepositoryMock) GetByID(id string) (*entities.Box, error) {
args := m.Called(id)

if args.Get(0) == nil {
return nil, args.Error(1)
}

return args.Get(0).(*entities.Box), args.Error(1)
}

func (m *BoxRepositoryMock) Update(box *entities.Box) error {
args := m.Called(box)
return args.Error(0)
}
Loading