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

Sunioatm/joh 29 pet endpoints #12

Merged
merged 8 commits into from
Jan 6, 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
17 changes: 11 additions & 6 deletions src/app/dto/adopt.dto.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package dto

import (
"github.com/google/uuid"
)

type AdoptDto struct {
UserID uuid.UUID `json:"user_id" validate:"required"`
PetID uuid.UUID `json:"pet_id" validate:"required"`
UserID string `json:"user_id" validate:"required"`
PetID string `json:"pet_id" validate:"required"`
}

type AdoptByRequest struct {
UserID string `json:"user_id" validate:"required"`
PetID string `json:"pet_id" validate:"required"`
}

type AdoptByResponse struct {
Success bool `json:"success"`
}
63 changes: 63 additions & 0 deletions src/app/handler/pet/pet.handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,3 +298,66 @@ func (h *Handler) Delete(c router.IContext) {
})
return
}

// Adopt is a function that handles the adoption of a pet in the database
// @Summary Adopt a pet
// @Description Return true if the pet is successfully adopted
// @Param id path string true "Pet ID"
// @Param user_id body string true "User ID"
// @Param pet_id body string true "Pet ID"
// @Tags pet
// @Accept json
// @Produce json
// @Success 201 {object} bool
// @Failure 400 {object} dto.ResponseBadRequestErr "Invalid request body"
// @Failure 500 {object} dto.ResponseInternalErr "Internal service error"
// @Failure 503 {object} dto.ResponseServiceDownErr "Service is down"
// @Router /v1/pets/{id}/adopt [put]
func (h *Handler) Adopt(c router.IContext) {
petId, err := c.Param("id")
if err != nil {
c.JSON(http.StatusBadRequest, &dto.ResponseErr{
StatusCode: http.StatusBadRequest,
Message: "Invalid ID",
Data: nil,
})
return
}

request := &dto.AdoptByRequest{}
err = c.Bind(request)
if err != nil {
c.JSON(http.StatusBadRequest, dto.ResponseErr{
StatusCode: http.StatusBadRequest,
Message: constant.BindingRequestErrorMessage + err.Error(),
Data: nil,
})
return
}

if err := h.validate.Validate(request); err != nil {
var errorMessage []string
for _, reqErr := range err {
errorMessage = append(errorMessage, reqErr.Message)
}
c.JSON(http.StatusBadRequest, dto.ResponseErr{
StatusCode: http.StatusBadRequest,
Message: constant.InvalidRequestBodyMessage + strings.Join(errorMessage, ", "),
Data: nil,
})
return
}

res, errRes := h.service.Adopt(petId, request)
if errRes != nil {
c.JSON(errRes.StatusCode, errRes)
return
}

c.JSON(http.StatusOK, dto.ResponseSuccess{
StatusCode: http.StatusOK,
Message: petconst.AdoptPetSuccessMessage,
Data: res,
})
return
}
74 changes: 74 additions & 0 deletions src/app/handler/pet/pet.handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type PetHandlerTest struct {
CreatePetRequest *dto.CreatePetRequest
ChangeViewPetRequest *dto.ChangeViewPetRequest
UpdatePetRequest *dto.UpdatePetRequest
AdoptByRequest *dto.AdoptByRequest
BindErr *dto.ResponseErr
NotFoundErr *dto.ResponseErr
ServiceDownErr *dto.ResponseErr
Expand Down Expand Up @@ -102,6 +103,8 @@ func (t *PetHandlerTest) SetupTest() {

t.ChangeViewPetRequest = &dto.ChangeViewPetRequest{}

t.AdoptByRequest = &dto.AdoptByRequest{}

t.ServiceDownErr = &dto.ResponseErr{
StatusCode: http.StatusServiceUnavailable,
Message: "Service is down",
Expand Down Expand Up @@ -449,3 +452,74 @@ func (t *PetHandlerTest) TestChangeViewGrpcErr() {
handler := NewHandler(petSvc, imageSvc, validator)
handler.ChangeView(context)
}

func (t *PetHandlerTest) TestAdoptSuccess() {
adoptByResponse := &dto.AdoptByResponse{
Success: true,
}
expectedResponse := dto.ResponseSuccess{
StatusCode: http.StatusOK,
Message: petConst.AdoptPetSuccessMessage,
Data: adoptByResponse,
}

controller := gomock.NewController(t.T())

petSvc := petMock.NewMockService(controller)
imageSvc := imageMock.NewMockService(controller)
validator := validatorMock.NewMockIDtoValidator(controller)
context := routerMock.NewMockIContext(controller)

context.EXPECT().Param("id").Return(t.Pet.Id, nil)
context.EXPECT().Bind(t.AdoptByRequest).Return(nil)
validator.EXPECT().Validate(t.AdoptByRequest).Return(nil)
petSvc.EXPECT().Adopt(t.Pet.Id, t.AdoptByRequest).Return(adoptByResponse, nil)
context.EXPECT().JSON(http.StatusOK, expectedResponse)

handler := NewHandler(petSvc, imageSvc, validator)
handler.Adopt(context)
}

func (t *PetHandlerTest) TestAdoptNotFound() {
adoptByResponse := &dto.AdoptByResponse{
Success: false,
}

controller := gomock.NewController(t.T())

petSvc := petMock.NewMockService(controller)
imageSvc := imageMock.NewMockService(controller)
validator := validatorMock.NewMockIDtoValidator(controller)
context := routerMock.NewMockIContext(controller)

context.EXPECT().Param("id").Return(t.Pet.Id, nil)
context.EXPECT().Bind(t.AdoptByRequest).Return(nil)
validator.EXPECT().Validate(t.AdoptByRequest).Return(nil)
petSvc.EXPECT().Adopt(t.Pet.Id, t.AdoptByRequest).Return(adoptByResponse, t.NotFoundErr)
context.EXPECT().JSON(http.StatusNotFound, t.NotFoundErr)

handler := NewHandler(petSvc, imageSvc, validator)
handler.Adopt(context)
}

func (t *PetHandlerTest) TestAdoptGrpcErr() {
adoptByResponse := &dto.AdoptByResponse{
Success: false,
}

controller := gomock.NewController(t.T())

petSvc := petMock.NewMockService(controller)
imageSvc := imageMock.NewMockService(controller)
validator := validatorMock.NewMockIDtoValidator(controller)
context := routerMock.NewMockIContext(controller)

context.EXPECT().Param("id").Return(t.Pet.Id, nil)
context.EXPECT().Bind(t.AdoptByRequest).Return(nil)
validator.EXPECT().Validate(t.AdoptByRequest).Return(nil)
petSvc.EXPECT().Adopt(t.Pet.Id, t.AdoptByRequest).Return(adoptByResponse, t.ServiceDownErr)
context.EXPECT().JSON(http.StatusServiceUnavailable, t.ServiceDownErr)

handler := NewHandler(petSvc, imageSvc, validator)
handler.Adopt(context)
}
49 changes: 44 additions & 5 deletions src/app/service/pet/pet.service.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,6 @@ type Service struct {
petClient petproto.PetServiceClient
}

// Adopt implements pet.Service.
func (*Service) Adopt(*dto.AdoptDto) (bool, *dto.ResponseErr) {
panic("unimplemented")
}

func NewService(petClient petproto.PetServiceClient) *Service {
return &Service{
petClient: petClient,
Expand Down Expand Up @@ -276,3 +271,47 @@ func (s *Service) ChangeView(id string, in *dto.ChangeViewPetRequest) (result *d
Success: res.Success,
}, nil
}

func (s *Service) Adopt(petId string, in *dto.AdoptByRequest) (result *dto.AdoptByResponse, err *dto.ResponseErr) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

res, errRes := s.petClient.AdoptPet(ctx, &petproto.AdoptPetRequest{
UserId: in.UserID,
PetId: in.PetID,
})
if errRes != nil {
st, _ := status.FromError(errRes)
log.Error().
Err(errRes).
Str("service", "pet").
Str("module", "adopt").
Msg(st.Message())
switch st.Code() {
case codes.NotFound:
return nil,
&dto.ResponseErr{
StatusCode: http.StatusNotFound,
Message: constant.PetNotFoundMessage,
Data: nil,
}
case codes.Unavailable:
return nil,
&dto.ResponseErr{
StatusCode: http.StatusServiceUnavailable,
Message: constant.UnavailableServiceMessage,
Data: nil,
}
default:
return nil,
&dto.ResponseErr{
StatusCode: http.StatusServiceUnavailable,
Message: constant.InternalErrorMessage,
Data: nil,
}
}
}
return &dto.AdoptByResponse{
Success: res.Success,
}, nil
}
56 changes: 56 additions & 0 deletions src/app/service/pet/pet.service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type PetServiceTest struct {
InvalidArgumentErr *dto.ResponseErr
InternalErr *dto.ResponseErr
ChangeViewedPetDto *dto.ChangeViewPetRequest
AdoptDto *dto.AdoptByRequest

Images []*imgproto.Image
ImagesList [][]*imgproto.Image
Expand Down Expand Up @@ -160,6 +161,11 @@ func (t *PetServiceTest) SetupTest() {
UserId: t.Pet.AdoptBy,
}

t.AdoptDto = &dto.AdoptByRequest{
UserID: t.Pet.AdoptBy,
PetID: t.Pet.Id,
}

t.UnavailableServiceErr = &dto.ResponseErr{
StatusCode: http.StatusServiceUnavailable,
Message: constant.UnavailableServiceMessage,
Expand Down Expand Up @@ -514,3 +520,53 @@ func (t *PetServiceTest) TestChangeViewUnavailableServiceError() {
assert.Equal(t.T(), &dto.ChangeViewPetResponse{Success: false}, actual)
assert.Equal(t.T(), expected, err)
}

func (t *PetServiceTest) TestAdoptSuccess() {
protoReq := t.AdoptReq
protoResp := &petproto.AdoptPetResponse{
Success: true,
}

client := &petmock.PetClientMock{}
client.On("AdoptPet", protoReq).Return(protoResp, nil)

svc := NewService(client)
actual, err := svc.Adopt(t.Pet.Id, t.AdoptDto)

assert.Nil(t.T(), err)
assert.Equal(t.T(), actual, &dto.AdoptByResponse{Success: true})
}

func (t *PetServiceTest) TestAdoptNotFoundError() {
protoReq := t.AdoptReq

clientErr := status.Error(codes.NotFound, constant.PetNotFoundMessage)

expected := t.NotFoundErr

client := &petmock.PetClientMock{}
client.On("AdoptPet", protoReq).Return(nil, clientErr)

svc := NewService(client)
actual, err := svc.Adopt(t.Pet.Id, t.AdoptDto)

assert.Nil(t.T(), actual)
assert.Equal(t.T(), expected, err)
}

func (t *PetServiceTest) TestAdoptUnavailableServiceError() {
protoReq := t.AdoptReq

clientErr := status.Error(codes.Unavailable, constant.UnavailableServiceMessage)

expected := t.UnavailableServiceErr

client := &petmock.PetClientMock{}
client.On("AdoptPet", protoReq).Return(nil, clientErr)

svc := NewService(client)
actual, err := svc.Adopt(t.Pet.Id, t.AdoptDto)

assert.Nil(t.T(), actual)
assert.Equal(t.T(), expected, err)
}
1 change: 1 addition & 0 deletions src/constant/pet/pet.constant.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,4 @@ const CreatePetSuccessMessage = "create pet success"
const UpdatePetSuccessMessage = "update pet success"
const ChangeViewPetSuccessMessage = "change view pet success"
const DeletePetSuccessMessage = "delete pet success"
const AdoptPetSuccessMessage = "adopt pet success"
1 change: 1 addition & 0 deletions src/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ func main() {
r.GetPet("/:id", petHandler.FindOne)
r.PostPet("/create", petHandler.Create)
r.PutPet("/:id", petHandler.Update)
r.PutPet("/:id/adopt", petHandler.Adopt)
r.PutPet("/:id/visible", petHandler.ChangeView)
r.DeletePet("/:id", petHandler.Delete)

Expand Down
10 changes: 5 additions & 5 deletions src/mocks/service/pet/pet.mock.go

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

2 changes: 1 addition & 1 deletion src/pkg/service/pet/pet.service.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ type Service interface {
Update(string, *dto.UpdatePetRequest) (*dto.PetResponse, *dto.ResponseErr)
Delete(string) (*dto.DeleteResponse, *dto.ResponseErr)
ChangeView(string, *dto.ChangeViewPetRequest) (*dto.ChangeViewPetResponse, *dto.ResponseErr)
Adopt(*dto.AdoptDto) (bool, *dto.ResponseErr)
Adopt(string, *dto.AdoptByRequest) (*dto.AdoptByResponse, *dto.ResponseErr)
}
Loading