diff --git a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md new file mode 100644 index 0000000..7748e23 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md @@ -0,0 +1,13 @@ +## Change made + +- [ ]  New features +- [ ]  Bug fixes +- [ ]  Breaking changes +## Describe what you have done +- +### New Features +- +### Fix +- +### Others +- \ No newline at end of file diff --git a/Makefile b/Makefile index 6e0a369..e54dade 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,7 @@ mock-gen: mockgen -source ./src/pkg/service/auth/auth.service.go -destination ./src/mocks/service/auth/auth.mock.go mockgen -source ./src/pkg/service/user/user.service.go -destination ./src/mocks/service/user/user.mock.go mockgen -source ./src/pkg/service/pet/pet.service.go -destination ./src/mocks/service/pet/pet.mock.go + mockgen -source ./src/pkg/service/like/like.service.go -destination ./src/mocks/service/like/like.mock.go mockgen -source ./src/pkg/service/image/image.service.go -destination ./src/mocks/service/image/image.mock.go mockgen -source ./src/app/validator/validator.go -destination ./src/mocks/validator/validator.mock.go mockgen -source ./src/app/router/context.go -destination ./src/mocks/router/context.mock.go diff --git a/config/auth/config.example.yaml b/config/auth/config.example.yaml index ad75341..50d989a 100644 --- a/config/auth/config.example.yaml +++ b/config/auth/config.example.yaml @@ -4,7 +4,7 @@ app: secret: database: - host: localhost + host: local-db port: 5432 name: johnjud_db username: root @@ -17,7 +17,7 @@ jwt: issuer: redis: - host: localhost + host: local-cache port: 6379 password: "" dbnum: 0 \ No newline at end of file diff --git a/config/config.example.yaml b/config/config.example.yaml index f5e400a..1a12b6b 100644 --- a/config/config.example.yaml +++ b/config/config.example.yaml @@ -3,6 +3,10 @@ app: debug: true service: - auth: auth:3002 - backend: backend:3003 - file: file:3004 \ No newline at end of file + auth: localhost:3002 + backend: localhost:3003 + file: localhost:3004 + # Production, when gateway in a container in same network + # auth: auth:3002 + # backend: backend:3003 + # file: file:3004 \ No newline at end of file diff --git a/go.mod b/go.mod index c0a6d31..c57ecda 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/gofiber/fiber/v2 v2.52.0 github.com/golang/mock v1.6.0 github.com/google/uuid v1.5.0 - github.com/isd-sgcu/johnjud-go-proto v0.2.2 + github.com/isd-sgcu/johnjud-go-proto v0.5.0 github.com/pkg/errors v0.9.1 github.com/rs/zerolog v1.31.0 github.com/spf13/viper v1.18.2 diff --git a/go.sum b/go.sum index d5858c6..67bd7d5 100644 --- a/go.sum +++ b/go.sum @@ -68,6 +68,10 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/isd-sgcu/johnjud-go-proto v0.2.2 h1:TOAmbwy/I+8/J5LPZH0ZN7lSLczBiZe1fs88gH8XrhY= github.com/isd-sgcu/johnjud-go-proto v0.2.2/go.mod h1:1OK6aiCgtXQiLhxp0r6iLEejYIRpckWQZDrCZ9Trbo4= +github.com/isd-sgcu/johnjud-go-proto v0.3.1 h1:WyWfzl+5nWOw3AmINtcTfojg4CJh8ZRNbZC6qA//OXU= +github.com/isd-sgcu/johnjud-go-proto v0.3.1/go.mod h1:1OK6aiCgtXQiLhxp0r6iLEejYIRpckWQZDrCZ9Trbo4= +github.com/isd-sgcu/johnjud-go-proto v0.5.0 h1:GgqRzWjya5p1yhfU/kpX8i4WL42+qT2TkyXZmssH6B4= +github.com/isd-sgcu/johnjud-go-proto v0.5.0/go.mod h1:1OK6aiCgtXQiLhxp0r6iLEejYIRpckWQZDrCZ9Trbo4= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= diff --git a/src/app/constant/error.constant.go b/src/app/constant/error.constant.go index cf607bc..5e378d3 100644 --- a/src/app/constant/error.constant.go +++ b/src/app/constant/error.constant.go @@ -8,6 +8,9 @@ const UnauthorizedMessage = "Unauthorized" const DuplicateEmailMessage = "Duplicate Email" const InternalErrorMessage = "Internal Error" const UnavailableServiceMessage = "Unavailable Service" +const InvalidIDMessage = "Invalid ID" +const InvalidArgumentMessage = "Invalid Argument" const PetNotFoundMessage = "Pet not found" -const InvalidArgument = "Invalid Argument" +const UserNotFoundMessage = "User not found" +const ImageNotFoundMessage = "Image not found" diff --git a/src/app/dto/adopt.dto.go b/src/app/dto/adopt.dto.go deleted file mode 100644 index 82a453d..0000000 --- a/src/app/dto/adopt.dto.go +++ /dev/null @@ -1,10 +0,0 @@ -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"` -} diff --git a/src/app/dto/common.dto.go b/src/app/dto/common.dto.go index ba64d2d..611c90e 100644 --- a/src/app/dto/common.dto.go +++ b/src/app/dto/common.dto.go @@ -67,9 +67,3 @@ type ResponseGatewayTimeoutErr struct { Message string `json:"message" example:"Connection timeout"` Data interface{} `json:"data"` } - -type ResponseSuccess struct { - StatusCode int `json:"status_code" example:"200"` - Message string `json:"message" example:"success"` - Data interface{} `json:"data"` -} diff --git a/src/app/dto/image.dto.go b/src/app/dto/image.dto.go index f30947a..98414f1 100644 --- a/src/app/dto/image.dto.go +++ b/src/app/dto/image.dto.go @@ -1,7 +1,26 @@ package dto -type ImageDto struct { +type ImageResponse struct { + Id string `json:"id"` + Url string `json:"url"` + ObjectKey string `json:"object_key"` +} + +type UploadImageRequest struct { Filename string `json:"filename" validate:"required"` Data []byte `json:"data" validate:"required"` PetId string `json:"pet_id" validate:"required"` } + +type DeleteImageResponse struct { + Success bool `json:"success"` +} + +type AssignPetRequest struct { + Ids []string `json:"ids" validate:"required"` + PetId string `json:"pet_id" validate:"required"` +} + +type AssignPetResponse struct { + Success bool `json:"success"` +} diff --git a/src/app/dto/like.dto.go b/src/app/dto/like.dto.go index 3caf974..9a1002f 100644 --- a/src/app/dto/like.dto.go +++ b/src/app/dto/like.dto.go @@ -1,10 +1,23 @@ package dto -import ( - "github.com/google/uuid" -) +type LikeResponse struct { + UserID string `json:"user_id"` + PetID string `json:"pet_id"` +} + +type FindLikeRequest struct { + UserID string `json:"user_id" validate:"required"` +} + +type CreateLikeRequest struct { + UserID string `json:"user_id" validate:"required"` + PetID string `json:"pet_id" validate:"required"` +} + +type DeleteLikeRequest struct { + Id string `json:"id" validate:"required"` +} -type LikeDto struct { - UserID uuid.UUID `json:"user_id" validate:"required"` - PetID uuid.UUID `json:"pet_id" validate:"required"` +type DeleteLikeResponse struct { + Success bool `json:"success"` } diff --git a/src/app/dto/pet.dto.go b/src/app/dto/pet.dto.go index 0d7c5b1..6455129 100644 --- a/src/app/dto/pet.dto.go +++ b/src/app/dto/pet.dto.go @@ -4,46 +4,65 @@ import ( "github.com/isd-sgcu/johnjud-gateway/src/constant/pet" ) -type ImageResponse struct { - Id string `json:"id"` - Url string `json:"url"` -} - type PetResponse struct { Id string `json:"id"` Type string `json:"type"` - Species string `json:"species"` Name string `json:"name"` Birthdate string `json:"birthdate"` Gender pet.Gender `json:"gender"` + Color string `json:"color"` + Pattern string `json:"pattern"` Habit string `json:"habit"` Caption string `json:"caption"` Status pet.Status `json:"status"` IsSterile *bool `json:"is_sterile"` IsVaccinated *bool `json:"is_vaccinated"` IsVisible *bool `json:"is_visible"` - IsClubPet *bool `json:"is_club_pet"` - Background string `json:"background"` + Origin string `json:"origin"` Address string `json:"address"` Contact string `json:"contact"` AdoptBy string `json:"adopt_by"` Images []ImageResponse `json:"images"` } +type FindAllPetRequest struct { + Search string `json:"search"` + Type string `json:"type"` + Gender string `json:"gender"` + Color string `json:"color"` + Pattern string `json:"pattern"` + Age string `json:"age"` + Origin string `json:"origin"` + PageSize int `json:"page_size"` + Page int `json:"page"` +} + +type FindAllMetadata struct { + Page int `json:"page"` + TotalPages int `json:"total_pages"` + PageSize int `json:"page_size"` + Total int `json:"total"` +} + +type FindAllPetResponse struct { + Pets []*PetResponse `json:"pets"` + Metadata *FindAllMetadata `json:"metadata"` +} + type CreatePetRequest struct { Type string `json:"type" validate:"required"` - Species string `json:"species" validate:"required"` Name string `json:"name" validate:"required"` Birthdate string `json:"birthdate" validate:"required"` Gender pet.Gender `json:"gender" validate:"required" example:"male"` + Color string `json:"color" validate:"required"` + Pattern string `json:"pattern" validate:"required"` Habit string `json:"habit" validate:"required"` Caption string `json:"caption"` Status pet.Status `json:"status" validate:"required" example:"findhome"` IsSterile *bool `json:"is_sterile" validate:"required"` IsVaccinated *bool `json:"is_vaccinated" validate:"required"` IsVisible *bool `json:"is_visible" validate:"required"` - IsClubPet *bool `json:"is_club_pet" validate:"required"` - Background string `json:"background"` + Origin string `json:"origin" validate:"required"` Address string `json:"address"` Contact string `json:"contact"` AdoptBy string `json:"adopt_by"` @@ -55,23 +74,32 @@ type ChangeViewPetRequest struct { } type ChangeViewPetResponse struct { - Success bool `json:"success" validate:"required"` + Success bool `json:"success"` +} + +type AdoptByRequest struct { + UserID string `json:"user_id" validate:"required"` + PetID string `json:"pet_id" validate:"required"` +} + +type AdoptByResponse struct { + Success bool `json:"success"` } type UpdatePetRequest struct { Type string `json:"type"` - Species string `json:"species"` Name string `json:"name"` Birthdate string `json:"birthdate"` Gender pet.Gender `json:"gender"` + Color string `json:"color"` + Pattern string `json:"pattern"` Habit string `json:"habit"` Caption string `json:"caption"` Status pet.Status `json:"status"` IsSterile *bool `json:"is_sterile"` IsVaccinated *bool `json:"is_vaccinated"` IsVisible *bool `json:"is_visible"` - IsClubPet *bool `json:"is_club_pet"` - Background string `json:"background"` + Origin string `json:"origin"` Address string `json:"address"` Contact string `json:"contact"` AdoptBy string `json:"adopt_by"` @@ -81,5 +109,5 @@ type DeleteRequest struct { Id string `json:"id" validate:"required"` } type DeleteResponse struct { - Success bool `json:"success" validate:"required"` + Success bool `json:"success"` } diff --git a/src/app/dto/user.dto.go b/src/app/dto/user.dto.go index 30d1320..8fc8b3a 100644 --- a/src/app/dto/user.dto.go +++ b/src/app/dto/user.dto.go @@ -1,14 +1,19 @@ package dto -type UserDto struct { +type User struct { + Id string `json:"id"` + Email string `json:"email"` + Firstname string `json:"firstname"` + Lastname string `json:"lastname"` +} + +type UpdateUserRequest struct { Email string `json:"email" validate:"required,email"` Password string `json:"password" validate:"required,gte=6,lte=30"` Firstname string `json:"firstname" validate:"required"` Lastname string `json:"lastname" validate:"required"` } -type UpdateUserDto struct { - Password string `json:"password" validate:"required,gte=6,lte=30"` - Firstname string `json:"firstname" validate:"required"` - Lastname string `json:"lastname" validate:"required"` +type DeleteUserResponse struct { + Success bool `json:"success"` } diff --git a/src/app/handler/image/image.handler.go b/src/app/handler/image/image.handler.go index 552498b..143fc9e 100644 --- a/src/app/handler/image/image.handler.go +++ b/src/app/handler/image/image.handler.go @@ -1,35 +1,133 @@ -package auth +package image import ( + "net/http" + "strings" + + "github.com/isd-sgcu/johnjud-gateway/src/app/constant" "github.com/isd-sgcu/johnjud-gateway/src/app/dto" "github.com/isd-sgcu/johnjud-gateway/src/app/router" "github.com/isd-sgcu/johnjud-gateway/src/app/validator" - proto "github.com/isd-sgcu/johnjud-go-proto/johnjud/file/image/v1" + imageSvc "github.com/isd-sgcu/johnjud-gateway/src/pkg/service/image" ) type Handler struct { - service Service - validate *validator.DtoValidator -} - -type Service interface { - FindByPetId(string) ([]*proto.Image, *dto.ResponseErr) - Upload(*dto.ImageDto) (*proto.Image, *dto.ResponseErr) - Delete(string) (bool, *dto.ResponseErr) + service imageSvc.Service + validate validator.IDtoValidator } -func NewHandler(service Service, validate *validator.DtoValidator) *Handler { +func NewHandler(service imageSvc.Service, validate validator.IDtoValidator) *Handler { return &Handler{service, validate} } func (h *Handler) FindByPetId(c *router.FiberCtx) { + id, err := c.ID() + if err != nil { + c.JSON(http.StatusBadRequest, dto.ResponseErr{ + StatusCode: http.StatusInternalServerError, + Message: constant.InvalidIDMessage, + Data: nil, + }) + return + } + response, respErr := h.service.FindByPetId(id) + if respErr != nil { + c.JSON(respErr.StatusCode, respErr) + return + } + + c.JSON(http.StatusOK, response) + return } func (h *Handler) Upload(c *router.FiberCtx) { + request := &dto.UploadImageRequest{} + 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 + } + + response, respErr := h.service.Upload(request) + if respErr != nil { + c.JSON(respErr.StatusCode, respErr) + return + } + c.JSON(http.StatusCreated, response) + return } func (h *Handler) Delete(c *router.FiberCtx) { + id, err := c.ID() + if err != nil { + c.JSON(http.StatusBadRequest, dto.ResponseErr{ + StatusCode: http.StatusBadRequest, + Message: err.Error(), + Data: nil, + }) + return + } + + res, errRes := h.service.Delete(id) + if errRes != nil { + c.JSON(errRes.StatusCode, errRes) + return + } + + c.JSON(http.StatusOK, res.Success) + return +} + +func (h *Handler) AssignPet(c *router.FiberCtx) { + request := &dto.AssignPetRequest{} + 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 + } + + response, respErr := h.service.AssignPet(request) + if respErr != nil { + c.JSON(respErr.StatusCode, respErr) + return + } + c.JSON(http.StatusOK, response) + return } diff --git a/src/app/handler/like/like.handler.go b/src/app/handler/like/like.handler.go index d01bf03..accafc0 100644 --- a/src/app/handler/like/like.handler.go +++ b/src/app/handler/like/like.handler.go @@ -1,35 +1,98 @@ package auth import ( + "net/http" + "strings" + + "github.com/isd-sgcu/johnjud-gateway/src/app/constant" "github.com/isd-sgcu/johnjud-gateway/src/app/dto" "github.com/isd-sgcu/johnjud-gateway/src/app/router" "github.com/isd-sgcu/johnjud-gateway/src/app/validator" - proto "github.com/isd-sgcu/johnjud-go-proto/johnjud/backend/like/v1" + likeSvc "github.com/isd-sgcu/johnjud-gateway/src/pkg/service/like" ) type Handler struct { - service Service - validate *validator.DtoValidator -} - -type Service interface { - FindByUserId(string) ([]*proto.Like, *dto.ResponseErr) - Create(*dto.LikeDto) (*proto.Like, *dto.ResponseErr) - Delete(string) (bool, *dto.ResponseErr) + service likeSvc.Service + validate validator.IDtoValidator } -func NewHandler(service Service, validate *validator.DtoValidator) *Handler { +func NewHandler(service likeSvc.Service, validate validator.IDtoValidator) *Handler { return &Handler{service, validate} } -func (h *Handler) FindByUserId(c *router.FiberCtx) { +func (h *Handler) FindByUserId(c router.IContext) { + id, err := c.Param("id") + if err != nil { + c.JSON(http.StatusBadRequest, dto.ResponseErr{ + StatusCode: http.StatusInternalServerError, + Message: constant.InvalidIDMessage, + Data: nil, + }) + return + } + + response, respErr := h.service.FindByUserId(id) + if respErr != nil { + c.JSON(respErr.StatusCode, respErr) + return + } + c.JSON(http.StatusOK, response) + return } -func (h *Handler) Create(c *router.FiberCtx) { +func (h *Handler) Create(c router.IContext) { + request := &dto.CreateLikeRequest{} + 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 + } + + response, respErr := h.service.Create(request) + if respErr != nil { + c.JSON(respErr.StatusCode, respErr) + return + } + + c.JSON(http.StatusCreated, response) + return } -func (h *Handler) Delete(c *router.FiberCtx) { +func (h *Handler) Delete(c router.IContext) { + id, err := c.Param("id") + if err != nil { + c.JSON(http.StatusBadRequest, dto.ResponseErr{ + StatusCode: http.StatusBadRequest, + Message: err.Error(), + Data: nil, + }) + return + } + + res, errRes := h.service.Delete(id) + if errRes != nil { + c.JSON(errRes.StatusCode, errRes) + return + } + c.JSON(http.StatusOK, res) + return } diff --git a/src/app/handler/like/like.handler_test.go b/src/app/handler/like/like.handler_test.go new file mode 100644 index 0000000..ed7debe --- /dev/null +++ b/src/app/handler/like/like.handler_test.go @@ -0,0 +1,267 @@ +package auth + +import ( + "net/http" + "testing" + + "github.com/bxcodec/faker/v4" + "github.com/golang/mock/gomock" + errConst "github.com/isd-sgcu/johnjud-gateway/src/app/constant" + "github.com/isd-sgcu/johnjud-gateway/src/app/dto" + utils "github.com/isd-sgcu/johnjud-gateway/src/app/utils/like" + routerMock "github.com/isd-sgcu/johnjud-gateway/src/mocks/router" + likeMock "github.com/isd-sgcu/johnjud-gateway/src/mocks/service/like" + validatorMock "github.com/isd-sgcu/johnjud-gateway/src/mocks/validator" + likeProto "github.com/isd-sgcu/johnjud-go-proto/johnjud/backend/like/v1" + "github.com/stretchr/testify/suite" +) + +type LikeHandlerTest struct { + suite.Suite + Likes []*likeProto.Like + Like *likeProto.Like + LikeResponse *dto.LikeResponse + CreateLikeRequest *dto.CreateLikeRequest + DeleteLikeRequest *dto.DeleteLikeRequest + NotFoundErr *dto.ResponseErr + + UnavailableServiceErr *dto.ResponseErr + InvalidArgumentErr *dto.ResponseErr + BindErr *dto.ResponseErr + InternalErr *dto.ResponseErr +} + +func TestLikeHandler(t *testing.T) { + suite.Run(t, new(LikeHandlerTest)) +} + +func (t *LikeHandlerTest) SetupTest() { + var likes []*likeProto.Like + for i := 0; i <= 3; i++ { + like := &likeProto.Like{ + Id: faker.UUIDDigit(), + UserId: faker.UUIDDigit(), + PetId: faker.UUIDDigit(), + } + likes = append(likes, like) + } + + t.Likes = likes + t.Like = likes[0] + + t.CreateLikeRequest = &dto.CreateLikeRequest{} + t.DeleteLikeRequest = &dto.DeleteLikeRequest{} + + t.NotFoundErr = &dto.ResponseErr{ + StatusCode: http.StatusNotFound, + Message: errConst.UserNotFoundMessage, + Data: nil, + } + + t.InternalErr = &dto.ResponseErr{ + StatusCode: http.StatusInternalServerError, + Message: errConst.InternalErrorMessage, + Data: nil, + } + + t.UnavailableServiceErr = &dto.ResponseErr{ + StatusCode: http.StatusServiceUnavailable, + Message: errConst.UnavailableServiceMessage, + Data: nil, + } +} + +func (t *LikeHandlerTest) TestFindLikesSuccess() { + findLikeResponse := utils.ProtoToDtoList(t.Likes) + expectedResponse := findLikeResponse + + controller := gomock.NewController(t.T()) + + likeSvc := likeMock.NewMockService(controller) + validator := validatorMock.NewMockIDtoValidator(controller) + context := routerMock.NewMockIContext(controller) + + context.EXPECT().Param("id").Return(t.Like.UserId, nil) + likeSvc.EXPECT().FindByUserId(t.Like.UserId).Return(utils.ProtoToDtoList(t.Likes), nil) + context.EXPECT().JSON(http.StatusOK, expectedResponse) + + handler := NewHandler(likeSvc, validator) + handler.FindByUserId(context) +} + +func (t *LikeHandlerTest) TestFindLikeNotFoundError() { + findLikeErrorResponse := t.NotFoundErr + + controller := gomock.NewController(t.T()) + + petSvc := likeMock.NewMockService(controller) + validator := validatorMock.NewMockIDtoValidator(controller) + context := routerMock.NewMockIContext(controller) + + context.EXPECT().Param("id").Return(t.Like.UserId, nil) + petSvc.EXPECT().FindByUserId(t.Like.UserId).Return(nil, findLikeErrorResponse) + context.EXPECT().JSON(http.StatusNotFound, findLikeErrorResponse) + + handler := NewHandler(petSvc, validator) + handler.FindByUserId(context) +} + +func (t *LikeHandlerTest) TestFindLikeServiceUnavailableError() { + findLikeErrorResponse := t.UnavailableServiceErr + + controller := gomock.NewController(t.T()) + + petSvc := likeMock.NewMockService(controller) + validator := validatorMock.NewMockIDtoValidator(controller) + context := routerMock.NewMockIContext(controller) + + context.EXPECT().Param("id").Return(t.Like.UserId, nil) + petSvc.EXPECT().FindByUserId(t.Like.UserId).Return(nil, findLikeErrorResponse) + context.EXPECT().JSON(http.StatusServiceUnavailable, findLikeErrorResponse) + + handler := NewHandler(petSvc, validator) + handler.FindByUserId(context) +} + +func (t *LikeHandlerTest) TestFindLikeInternalError() { + findLikeErrorResponse := t.InternalErr + + controller := gomock.NewController(t.T()) + + petSvc := likeMock.NewMockService(controller) + validator := validatorMock.NewMockIDtoValidator(controller) + context := routerMock.NewMockIContext(controller) + + context.EXPECT().Param("id").Return(t.Like.UserId, nil) + petSvc.EXPECT().FindByUserId(t.Like.UserId).Return(nil, findLikeErrorResponse) + context.EXPECT().JSON(http.StatusInternalServerError, findLikeErrorResponse) + + handler := NewHandler(petSvc, validator) + handler.FindByUserId(context) +} + +func (t *LikeHandlerTest) TestCreateSuccess() { + createLikeResponse := utils.ProtoToDto(t.Like) + expectedResponse := createLikeResponse + + controller := gomock.NewController(t.T()) + + likeSvc := likeMock.NewMockService(controller) + validator := validatorMock.NewMockIDtoValidator(controller) + context := routerMock.NewMockIContext(controller) + + context.EXPECT().Bind(t.CreateLikeRequest).Return(nil) + validator.EXPECT().Validate(t.CreateLikeRequest).Return(nil) + likeSvc.EXPECT().Create(t.CreateLikeRequest).Return(createLikeResponse, nil) + context.EXPECT().JSON(http.StatusCreated, expectedResponse) + + handler := NewHandler(likeSvc, validator) + handler.Create(context) +} + +func (t *LikeHandlerTest) TestCreateUnavailableServiceError() { + createLikeErrorResponse := t.UnavailableServiceErr + + controller := gomock.NewController(t.T()) + + likeSvc := likeMock.NewMockService(controller) + validator := validatorMock.NewMockIDtoValidator(controller) + context := routerMock.NewMockIContext(controller) + + context.EXPECT().Bind(t.CreateLikeRequest).Return(nil) + validator.EXPECT().Validate(t.CreateLikeRequest).Return(nil) + likeSvc.EXPECT().Create(t.CreateLikeRequest).Return(nil, createLikeErrorResponse) + context.EXPECT().JSON(http.StatusServiceUnavailable, createLikeErrorResponse) + + handler := NewHandler(likeSvc, validator) + handler.Create(context) +} + +func (t *LikeHandlerTest) TestCreateInternalError() { + createLikeErrorResponse := t.InternalErr + + controller := gomock.NewController(t.T()) + + likeSvc := likeMock.NewMockService(controller) + validator := validatorMock.NewMockIDtoValidator(controller) + context := routerMock.NewMockIContext(controller) + + context.EXPECT().Bind(t.CreateLikeRequest).Return(nil) + validator.EXPECT().Validate(t.CreateLikeRequest).Return(nil) + likeSvc.EXPECT().Create(t.CreateLikeRequest).Return(nil, createLikeErrorResponse) + context.EXPECT().JSON(http.StatusInternalServerError, createLikeErrorResponse) + + handler := NewHandler(likeSvc, validator) + handler.Create(context) +} + +func (t *LikeHandlerTest) TestDeleteSuccess() { + deleteResponse := &dto.DeleteLikeResponse{ + Success: true, + } + expectedResponse := deleteResponse + + controller := gomock.NewController(t.T()) + + likeSvc := likeMock.NewMockService(controller) + validator := validatorMock.NewMockIDtoValidator(controller) + context := routerMock.NewMockIContext(controller) + + context.EXPECT().Param("id").Return(t.Like.UserId, nil) + likeSvc.EXPECT().Delete(t.Like.UserId).Return(deleteResponse, nil) + context.EXPECT().JSON(http.StatusOK, expectedResponse) + + handler := NewHandler(likeSvc, validator) + handler.Delete(context) +} + +func (t *LikeHandlerTest) TestDeleteNotFoundError() { + deleteErrorResponse := t.NotFoundErr + + controller := gomock.NewController(t.T()) + + petSvc := likeMock.NewMockService(controller) + validator := validatorMock.NewMockIDtoValidator(controller) + context := routerMock.NewMockIContext(controller) + + context.EXPECT().Param("id").Return(t.Like.UserId, nil) + petSvc.EXPECT().FindByUserId(t.Like.UserId).Return(nil, deleteErrorResponse) + context.EXPECT().JSON(http.StatusNotFound, deleteErrorResponse) + + handler := NewHandler(petSvc, validator) + handler.FindByUserId(context) +} + +func (t *LikeHandlerTest) TestDeleteServiceUnavailableError() { + deleteErrorResponse := t.UnavailableServiceErr + + controller := gomock.NewController(t.T()) + + petSvc := likeMock.NewMockService(controller) + validator := validatorMock.NewMockIDtoValidator(controller) + context := routerMock.NewMockIContext(controller) + + context.EXPECT().Param("id").Return(t.Like.UserId, nil) + petSvc.EXPECT().FindByUserId(t.Like.UserId).Return(nil, deleteErrorResponse) + context.EXPECT().JSON(http.StatusServiceUnavailable, deleteErrorResponse) + + handler := NewHandler(petSvc, validator) + handler.FindByUserId(context) +} + +func (t *LikeHandlerTest) TestDeleteInternalError() { + deleteErrorResponse := t.InternalErr + + controller := gomock.NewController(t.T()) + + petSvc := likeMock.NewMockService(controller) + validator := validatorMock.NewMockIDtoValidator(controller) + context := routerMock.NewMockIContext(controller) + + context.EXPECT().Param("id").Return(t.Like.UserId, nil) + petSvc.EXPECT().FindByUserId(t.Like.UserId).Return(nil, deleteErrorResponse) + context.EXPECT().JSON(http.StatusInternalServerError, deleteErrorResponse) + + handler := NewHandler(petSvc, validator) + handler.FindByUserId(context) +} diff --git a/src/app/handler/pet/pet.handler.go b/src/app/handler/pet/pet.handler.go index a31ca6a..b9689c8 100644 --- a/src/app/handler/pet/pet.handler.go +++ b/src/app/handler/pet/pet.handler.go @@ -6,64 +6,58 @@ import ( "github.com/isd-sgcu/johnjud-gateway/src/app/constant" "github.com/isd-sgcu/johnjud-gateway/src/app/dto" - imageSvc "github.com/isd-sgcu/johnjud-gateway/src/app/handler/image" "github.com/isd-sgcu/johnjud-gateway/src/app/router" + petUtils "github.com/isd-sgcu/johnjud-gateway/src/app/utils/pet" "github.com/isd-sgcu/johnjud-gateway/src/app/validator" - petconst "github.com/isd-sgcu/johnjud-gateway/src/constant/pet" + imageSvc "github.com/isd-sgcu/johnjud-gateway/src/pkg/service/image" + petSvc "github.com/isd-sgcu/johnjud-gateway/src/pkg/service/pet" ) type Handler struct { - service Service + service petSvc.Service imageService imageSvc.Service validate validator.IDtoValidator } -type Service interface { - FindAll() ([]*dto.PetResponse, *dto.ResponseErr) - FindOne(string) (*dto.PetResponse, *dto.ResponseErr) - Create(*dto.CreatePetRequest) (*dto.PetResponse, *dto.ResponseErr) - Update(string, *dto.UpdatePetRequest) (*dto.PetResponse, *dto.ResponseErr) - ChangeView(string, *dto.ChangeViewPetRequest) (*dto.ChangeViewPetResponse, *dto.ResponseErr) - Delete(string) (*dto.DeleteResponse, *dto.ResponseErr) -} - -func NewHandler(service Service, imageService imageSvc.Service, validate validator.IDtoValidator) *Handler { +func NewHandler(service petSvc.Service, imageService imageSvc.Service, validate validator.IDtoValidator) *Handler { return &Handler{service, imageService, validate} } -// FindAll is a function that return all pets in database -// @Summary find all pets -// @Description Return the data of pets if successfully -// @Tags auth +// FindAll is a function that returns all pets in database +// @Summary finds all pets +// @Description Returns the data of pets if successful +// @Tags pet // @Accept json // @Produce json -// @Success 200 {object} dto.PetDto +// @Success 200 {object} []dto.PetResponse // @Failure 500 {object} dto.ResponseInternalErr "Internal service error" // @Failure 503 {object} dto.ResponseServiceDownErr "Service is down" // @Router /v1/pets/ [get] func (h *Handler) FindAll(c router.IContext) { - response, respErr := h.service.FindAll() + queries := c.Queries() + request, err := petUtils.QueriesToFindAllDto(queries) + if err != nil { + c.JSON(http.StatusBadRequest, err) + } + + response, respErr := h.service.FindAll(request) if respErr != nil { c.JSON(respErr.StatusCode, respErr) return } - c.JSON(http.StatusOK, dto.ResponseSuccess{ - StatusCode: http.StatusOK, - Message: petconst.FindAllPetSuccessMessage, - Data: response, - }) + c.JSON(http.StatusOK, response) return } -// FindOne is a function that return all pet in database -// @Summary find one pet -// @Description Return the data of pets if successfully +// FindOne is a function that returns a pet by id in database +// @Summary finds one pet +// @Description Returns the data of a pet if successful // @Param id path string true "pet id" -// @Tags auth +// @Tags pet // @Accept json // @Produce json -// @Success 200 {object} dto.PetDto +// @Success 200 {object} dto.PetResponse // @Failure 400 {object} dto.ResponseBadRequestErr "Invalid request body" // @Failure 500 {object} dto.ResponseInternalErr "Internal service error" // @Failure 503 {object} dto.ResponseServiceDownErr "Service is down" @@ -73,7 +67,7 @@ func (h *Handler) FindOne(c router.IContext) { if err != nil { c.JSON(http.StatusInternalServerError, dto.ResponseErr{ StatusCode: http.StatusInternalServerError, - Message: "Invalid ID", + Message: constant.InvalidIDMessage, Data: nil, }) return @@ -85,22 +79,18 @@ func (h *Handler) FindOne(c router.IContext) { return } - c.JSON(http.StatusOK, dto.ResponseSuccess{ - StatusCode: http.StatusOK, - Message: petconst.FindOnePetSuccessMessage, - Data: response, - }) + c.JSON(http.StatusOK, response) return } -// Create is a function that create pet in database -// @Summary create pet -// @Description Return the data of pet if successfully +// Create is a function that creates pet in database +// @Summary creates pet +// @Description Returns the data of pet if successful // @Param create body dto.CreatePetRequest true "pet dto" -// @Tags auth +// @Tags pet // @Accept json // @Produce json -// @Success 201 {object} dto.PetDto +// @Success 201 {object} dto.PetResponse // @Failure 400 {object} dto.ResponseBadRequestErr "Invalid request body" // @Failure 500 {object} dto.ResponseInternalErr "Internal service error" // @Failure 503 {object} dto.ResponseServiceDownErr "Service is down" @@ -136,23 +126,19 @@ func (h *Handler) Create(c router.IContext) { return } - c.JSON(http.StatusCreated, dto.ResponseSuccess{ - StatusCode: http.StatusCreated, - Message: petconst.CreatePetSuccessMessage, - Data: response, - }) + c.JSON(http.StatusCreated, response) return } -// Update is a function that update pet in database -// @Summary update pet -// @Description Return the data of pet if successfully +// Update is a function that updates pet in database +// @Summary updates pet +// @Description Returns the data of pet if successfully // @Param update body dto.UpdatePetRequest true "update pet dto" -// @Param id path stirng true "pet id" -// @Tags auth +// @Param id path string true "pet id" +// @Tags pet // @Accept json // @Produce json -// @Success 201 {object} dto.PetDto +// @Success 201 {object} dto.PetResponse // @Failure 400 {object} dto.ResponseBadRequestErr "Invalid request body" // @Failure 500 {object} dto.ResponseInternalErr "Internal service error" // @Failure 503 {object} dto.ResponseServiceDownErr "Service is down" @@ -199,27 +185,23 @@ func (h *Handler) Update(c router.IContext) { return } - c.JSON(http.StatusOK, dto.ResponseSuccess{ - StatusCode: http.StatusOK, - Message: petconst.UpdatePetSuccessMessage, - Data: pet, - }) + c.JSON(http.StatusOK, pet) return } -// Change is a function that change visibility of pet in database -// @Summary change view pet -// @Description Return the status true of pet if successfully else false -// @Param change view body dto.ChangeViewPetRequest true "change view pet dto" -// @Param id string true "pet id" -// @Tags auth +// ChangeView is a function that changes visibility of pet in database +// @Summary changes pet's public visiblility +// @Description Returns successful status if pet's IsVisible is successfully changed +// @Param changeViewDto body dto.ChangeViewPetRequest true "changeView pet dto" +// @Param id path string true "pet id" +// @Tags pet // @Accept json // @Produce json -// @Success 201 {object} bool +// @Success 201 {object} dto.ChangeViewPetResponse // @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/ [put] +// @Router /v1/pets/{id}/visible [put] func (h *Handler) ChangeView(c router.IContext) { id, err := c.Param("id") if err != nil { @@ -262,22 +244,18 @@ func (h *Handler) ChangeView(c router.IContext) { return } - c.JSON(http.StatusOK, dto.ResponseSuccess{ - StatusCode: http.StatusOK, - Message: petconst.ChangeViewPetSuccessMessage, - Data: res, - }) + c.JSON(http.StatusOK, res) return } -// Delete is a function that delete pet in database -// @Summary delete pet -// @Description Return the status true of pet if successfully else false -// @Param id string true "pet id" -// @Tags auth +// Delete is a function that deletes pet in database +// @Summary deletes pet +// @Description Returns successful status if pet is successfully deleted +// @Param id path string true "pet id" +// @Tags pet // @Accept json // @Produce json -// @Success 201 {object} bool +// @Success 201 {object} dto.DeleteResponse // @Failure 400 {object} dto.ResponseBadRequestErr "Invalid request body" // @Failure 500 {object} dto.ResponseInternalErr "Internal service error" // @Failure 503 {object} dto.ResponseServiceDownErr "Service is down" @@ -299,10 +277,64 @@ func (h *Handler) Delete(c router.IContext) { return } - c.JSON(http.StatusOK, dto.ResponseSuccess{ - StatusCode: http.StatusOK, - Message: petconst.DeletePetSuccessMessage, - Data: res, - }) + c.JSON(http.StatusOK, res) + return +} + +// Adopt is a function that handles the adoption of a pet in the database +// @Summary Change a pet's adoptBy status +// @Description Return true if the pet is successfully adopted +// @Param adoptDto body dto.AdoptByRequest true "adopt pet dto" +// @Param id path string true "Pet ID" +// @Tags pet +// @Accept json +// @Produce json +// @Success 201 {object} dto.AdoptByResponse +// @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, res) return } diff --git a/src/app/handler/pet/pet.handler_test.go b/src/app/handler/pet/pet.handler_test.go index 6c4c903..e42dfbb 100644 --- a/src/app/handler/pet/pet.handler_test.go +++ b/src/app/handler/pet/pet.handler_test.go @@ -14,10 +14,12 @@ import ( petMock "github.com/isd-sgcu/johnjud-gateway/src/mocks/service/pet" validatorMock "github.com/isd-sgcu/johnjud-gateway/src/mocks/validator" + errConst "github.com/isd-sgcu/johnjud-gateway/src/app/constant" utils "github.com/isd-sgcu/johnjud-gateway/src/app/utils/pet" - petconst "github.com/isd-sgcu/johnjud-gateway/src/constant/pet" + petConst "github.com/isd-sgcu/johnjud-gateway/src/constant/pet" petProto "github.com/isd-sgcu/johnjud-go-proto/johnjud/backend/pet/v1" - imageProto "github.com/isd-sgcu/johnjud-go-proto/johnjud/file/image/v1" + imgProto "github.com/isd-sgcu/johnjud-go-proto/johnjud/file/image/v1" + "github.com/stretchr/testify/suite" ) @@ -26,15 +28,19 @@ type PetHandlerTest struct { Pet *petProto.Pet Pets []*petProto.Pet PetDto *dto.PetResponse + QueriesMock map[string]string + Metadata *dto.FindAllMetadata + FindAllPetRequest *dto.FindAllPetRequest CreatePetRequest *dto.CreatePetRequest ChangeViewPetRequest *dto.ChangeViewPetRequest UpdatePetRequest *dto.UpdatePetRequest + AdoptByRequest *dto.AdoptByRequest BindErr *dto.ResponseErr NotFoundErr *dto.ResponseErr ServiceDownErr *dto.ResponseErr InternalErr *dto.ResponseErr - Images []*imageProto.Image - ImagesList [][]*imageProto.Image + Images []*imgProto.Image + ImagesList [][]*imgProto.Image } func TestPetHandler(t *testing.T) { @@ -46,23 +52,26 @@ func (t *PetHandlerTest) SetupTest() { t.ImagesList = imagesList t.Images = imagesList[0] var pets []*petProto.Pet + genders := []petConst.Gender{petConst.MALE, petConst.FEMALE} + statuses := []petConst.Status{petConst.ADOPTED, petConst.FINDHOME} + for i := 0; i <= 3; i++ { pet := &petProto.Pet{ Id: faker.UUIDDigit(), Type: faker.Word(), - Species: faker.Word(), Name: faker.Name(), Birthdate: faker.Word(), - Gender: petProto.Gender(rand.Intn(1) + 1), + Gender: string(genders[rand.Intn(2)]), + Color: faker.Word(), + Pattern: faker.Word(), Habit: faker.Paragraph(), Caption: faker.Paragraph(), - Images: []*imageProto.Image{}, - Status: petProto.PetStatus(rand.Intn(1) + 1), + Images: []*imgProto.Image{}, + Status: string(statuses[rand.Intn(2)]), IsSterile: true, IsVaccinated: true, IsVisible: true, - IsClubPet: true, - Background: faker.Paragraph(), + Origin: faker.Paragraph(), Address: faker.Paragraph(), Contact: faker.Paragraph(), AdoptBy: "", @@ -74,32 +83,55 @@ func (t *PetHandlerTest) SetupTest() { t.Pets = pets t.Pet = t.Pets[0] + t.Metadata = &dto.FindAllMetadata{ + Page: 1, + TotalPages: 1, + PageSize: len(t.Pets), + Total: len(t.Pets), + } + t.PetDto = &dto.PetResponse{ Id: t.Pet.Id, Type: t.Pet.Type, - Species: t.Pet.Species, Name: t.Pet.Name, Birthdate: t.Pet.Birthdate, Gender: pet.Gender(t.Pet.Gender), + Color: t.Pet.Color, + Pattern: t.Pet.Pattern, Habit: t.Pet.Habit, Caption: t.Pet.Caption, Status: pet.Status(t.Pet.Status), IsSterile: &t.Pet.IsSterile, IsVaccinated: &t.Pet.IsVaccinated, IsVisible: &t.Pet.IsVisible, - IsClubPet: &t.Pet.IsClubPet, - Background: t.Pet.Background, + Origin: t.Pet.Origin, Address: t.Pet.Address, Contact: t.Pet.Contact, AdoptBy: t.Pet.AdoptBy, } + t.QueriesMock = map[string]string{ + "search": "", + "type": "", + "gender": "", + "color": "", + "pattern": "", + "age": "", + "origin": "", + "pageSize": "0", + "page": "0", + } + + t.FindAllPetRequest = &dto.FindAllPetRequest{} + t.CreatePetRequest = &dto.CreatePetRequest{} t.UpdatePetRequest = &dto.UpdatePetRequest{} t.ChangeViewPetRequest = &dto.ChangeViewPetRequest{} + t.AdoptByRequest = &dto.AdoptByRequest{} + t.ServiceDownErr = &dto.ResponseErr{ StatusCode: http.StatusServiceUnavailable, Message: "Service is down", @@ -108,28 +140,28 @@ func (t *PetHandlerTest) SetupTest() { t.NotFoundErr = &dto.ResponseErr{ StatusCode: http.StatusNotFound, - Message: "Pet not found", + Message: errConst.PetNotFoundMessage, Data: nil, } t.BindErr = &dto.ResponseErr{ StatusCode: http.StatusBadRequest, - Message: "Invalid ID", + Message: errConst.InvalidIDMessage, } t.InternalErr = &dto.ResponseErr{ StatusCode: http.StatusInternalServerError, - Message: "Internal Server Error", + Message: errConst.InternalErrorMessage, Data: nil, } } func (t *PetHandlerTest) TestFindAllSuccess() { - findAllResponse := utils.RawToDtoList(t.Pets, t.ImagesList) - expectedResponse := dto.ResponseSuccess{ - StatusCode: http.StatusOK, - Message: petconst.FindAllPetSuccessMessage, - Data: findAllResponse, + findAllResponse := utils.ProtoToDtoList(t.Pets, t.ImagesList) + metadataResponse := t.Metadata + expectedResponse := &dto.FindAllPetResponse{ + Pets: findAllResponse, + Metadata: metadataResponse, } controller := gomock.NewController(t.T()) @@ -139,7 +171,8 @@ func (t *PetHandlerTest) TestFindAllSuccess() { validator := validatorMock.NewMockIDtoValidator(controller) context := routerMock.NewMockIContext(controller) - petSvc.EXPECT().FindAll().Return(findAllResponse, nil) + context.EXPECT().Queries().Return(t.QueriesMock) + petSvc.EXPECT().FindAll(t.FindAllPetRequest).Return(expectedResponse, nil) context.EXPECT().JSON(http.StatusOK, expectedResponse) handler := NewHandler(petSvc, imageSvc, validator) @@ -148,11 +181,7 @@ func (t *PetHandlerTest) TestFindAllSuccess() { func (t *PetHandlerTest) TestFindOneSuccess() { findOneResponse := utils.ProtoToDto(t.Pet, t.Images) - expectedResponse := dto.ResponseSuccess{ - StatusCode: http.StatusOK, - Message: petconst.FindOnePetSuccessMessage, - Data: findOneResponse, - } + expectedResponse := findOneResponse controller := gomock.NewController(t.T()) @@ -207,11 +236,7 @@ func (t *PetHandlerTest) TestFindOneGrpcErr() { func (t *PetHandlerTest) TestCreateSuccess() { createResponse := utils.ProtoToDto(t.Pet, t.Images) - expectedResponse := dto.ResponseSuccess{ - StatusCode: http.StatusCreated, - Message: petconst.CreatePetSuccessMessage, - Data: createResponse, - } + expectedResponse := createResponse controller := gomock.NewController(t.T()) @@ -250,11 +275,7 @@ func (t *PetHandlerTest) TestCreateGrpcErr() { func (t *PetHandlerTest) TestUpdateSuccess() { updateResponse := utils.ProtoToDto(t.Pet, t.Images) - expectedResponse := dto.ResponseSuccess{ - StatusCode: http.StatusOK, - Message: petconst.UpdatePetSuccessMessage, - Data: updateResponse, - } + expectedResponse := updateResponse controller := gomock.NewController(t.T()) @@ -317,11 +338,7 @@ func (t *PetHandlerTest) TestDeleteSuccess() { deleteResponse := &dto.DeleteResponse{ Success: true, } - expectedResponse := dto.ResponseSuccess{ - StatusCode: http.StatusOK, - Message: petconst.DeletePetSuccessMessage, - Data: deleteResponse, - } + expectedResponse := deleteResponse controller := gomock.NewController(t.T()) @@ -381,11 +398,7 @@ func (t *PetHandlerTest) TestChangeViewSuccess() { changeViewResponse := &dto.ChangeViewPetResponse{ Success: true, } - expectedResponse := dto.ResponseSuccess{ - StatusCode: http.StatusOK, - Message: petconst.ChangeViewPetSuccessMessage, - Data: changeViewResponse, - } + expectedResponse := changeViewResponse controller := gomock.NewController(t.T()) @@ -447,3 +460,70 @@ func (t *PetHandlerTest) TestChangeViewGrpcErr() { handler := NewHandler(petSvc, imageSvc, validator) handler.ChangeView(context) } + +func (t *PetHandlerTest) TestAdoptSuccess() { + adoptByResponse := &dto.AdoptByResponse{ + Success: true, + } + expectedResponse := 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) +} diff --git a/src/app/handler/user/user.handler.go b/src/app/handler/user/user.handler.go index 9e25ed6..4277361 100644 --- a/src/app/handler/user/user.handler.go +++ b/src/app/handler/user/user.handler.go @@ -1,11 +1,14 @@ package user import ( + "net/http" + "strings" + + "github.com/isd-sgcu/johnjud-gateway/src/app/constant" "github.com/isd-sgcu/johnjud-gateway/src/app/dto" "github.com/isd-sgcu/johnjud-gateway/src/app/router" "github.com/isd-sgcu/johnjud-gateway/src/app/validator" "github.com/isd-sgcu/johnjud-gateway/src/pkg/service/user" - "net/http" ) type Handler struct { @@ -17,12 +20,24 @@ func NewHandler(service user.Service, validate validator.IDtoValidator) *Handler return &Handler{service, validate} } -func (h *Handler) FindOne(c *router.FiberCtx) { +// FindOne is a function that returns a user by id from database +// @Summary finds one user +// @Description Returns the data of user if successful +// @Param id path string true "user id" +// @Tags user +// @Accept json +// @Produce json +// @Success 200 {object} dto.User +// @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/users/{id} [get] +func (h *Handler) FindOne(c router.IContext) { id, err := c.ID() if err != nil { - c.JSON(http.StatusInternalServerError, dto.ResponseErr{ - StatusCode: http.StatusInternalServerError, - Message: "Invalid ID", + c.JSON(http.StatusBadRequest, &dto.ResponseErr{ + StatusCode: http.StatusBadRequest, + Message: err.Error(), Data: nil, }) return @@ -34,22 +49,51 @@ func (h *Handler) FindOne(c *router.FiberCtx) { return } - c.JSON(http.StatusCreated, user) + c.JSON(http.StatusOK, user) return } -func (h *Handler) Update(c *router.FiberCtx) { +// Update is a function that updates user in database +// @Summary updates user +// @Description Returns the data of user if successfully +// @Param update body dto.UpdateUserRequest true "update user dto" +// @Tags auth +// @Accept json +// @Produce json +// @Success 201 {object} dto.User +// @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/users [put] +func (h *Handler) Update(c router.IContext) { usrId := c.UserID() - usrDto := dto.UpdateUserDto{} + request := &dto.UpdateUserRequest{} - err := c.Bind(&usrDto) + err := c.Bind(request) if err != nil { - c.JSON(http.StatusBadRequest, err) + c.JSON(http.StatusBadRequest, &dto.ResponseErr{ + StatusCode: http.StatusBadRequest, + Message: constant.BindingRequestErrorMessage + err.Error(), + Data: nil, + }) return } - user, errRes := h.service.Update(usrId, &usrDto) + 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 + } + + user, errRes := h.service.Update(usrId, request) if errRes != nil { c.JSON(errRes.StatusCode, errRes) return @@ -58,3 +102,37 @@ func (h *Handler) Update(c *router.FiberCtx) { c.JSON(http.StatusOK, user) return } + +// Delete is a function that deletes user in database +// @Summary deletes user +// @Description Returns successful status if user is successfully deleted +// @Param id path string true "user id" +// @Tags user +// @Accept json +// @Produce json +// @Success 201 {object} bool +// @Success 201 {object} dto.DeleteUserResponse +// @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/users/{id} [delete] +func (h *Handler) Delete(c router.IContext) { + id, err := c.ID() + if err != nil { + c.JSON(http.StatusBadRequest, &dto.ResponseErr{ + StatusCode: http.StatusBadRequest, + Message: err.Error(), + Data: nil, + }) + return + } + + res, errRes := h.service.Delete(id) + if errRes != nil { + c.JSON(errRes.StatusCode, errRes) + return + } + + c.JSON(http.StatusOK, res) + return +} diff --git a/src/app/handler/user/user.handler_test.go b/src/app/handler/user/user.handler_test.go new file mode 100644 index 0000000..8757d2f --- /dev/null +++ b/src/app/handler/user/user.handler_test.go @@ -0,0 +1,365 @@ +package user + +import ( + "errors" + "net/http" + "strings" + "testing" + + "github.com/go-faker/faker/v4" + "github.com/golang/mock/gomock" + "github.com/isd-sgcu/johnjud-gateway/src/app/constant" + "github.com/isd-sgcu/johnjud-gateway/src/app/dto" + routerMock "github.com/isd-sgcu/johnjud-gateway/src/mocks/router" + userMock "github.com/isd-sgcu/johnjud-gateway/src/mocks/service/user" + validatorMock "github.com/isd-sgcu/johnjud-gateway/src/mocks/validator" + + errConst "github.com/isd-sgcu/johnjud-gateway/src/app/constant" + proto "github.com/isd-sgcu/johnjud-go-proto/johnjud/auth/user/v1" + + "github.com/stretchr/testify/suite" +) + +type UserHandlerTest struct { + suite.Suite + User *proto.User + UserDto *dto.User + UpdateUserRequest *dto.UpdateUserRequest + BindErr *dto.ResponseErr + NotFoundErr *dto.ResponseErr + InvalidIDErr *dto.ResponseErr + ServiceDownErr *dto.ResponseErr + DuplicateEmailErr *dto.ResponseErr + InternalErr *dto.ResponseErr +} + +func TestUserHandler(t *testing.T) { + suite.Run(t, new(UserHandlerTest)) +} + +func (t *UserHandlerTest) SetupTest() { + t.User = &proto.User{ + Id: faker.UUIDDigit(), + Email: faker.Email(), + Password: faker.Password(), + Firstname: faker.FirstName(), + Lastname: faker.LastName(), + Role: "user", + } + t.UserDto = &dto.User{ + Id: t.User.Id, + Firstname: t.User.Firstname, + Lastname: t.User.Lastname, + Email: t.User.Email, + } + + t.UpdateUserRequest = &dto.UpdateUserRequest{} + + t.ServiceDownErr = &dto.ResponseErr{ + StatusCode: http.StatusServiceUnavailable, + Message: "Service is down", + Data: nil, + } + + t.NotFoundErr = &dto.ResponseErr{ + StatusCode: http.StatusNotFound, + Message: errConst.UserNotFoundMessage, + Data: nil, + } + + t.InvalidIDErr = &dto.ResponseErr{ + StatusCode: http.StatusBadRequest, + Message: errConst.InvalidIDMessage, + Data: nil, + } + + t.BindErr = &dto.ResponseErr{ + StatusCode: http.StatusBadRequest, + Message: errConst.InvalidIDMessage, + } + + t.DuplicateEmailErr = &dto.ResponseErr{ + StatusCode: http.StatusConflict, + Message: errConst.DuplicateEmailMessage, + Data: nil, + } + + t.InternalErr = &dto.ResponseErr{ + StatusCode: http.StatusInternalServerError, + Message: errConst.InternalErrorMessage, + Data: nil, + } +} + +func (t *UserHandlerTest) TestFindOneSuccess() { + svcResp := t.UserDto + expectedResp := t.UserDto + + controller := gomock.NewController(t.T()) + + userSvc := userMock.NewMockService(controller) + validator := validatorMock.NewMockIDtoValidator(controller) + context := routerMock.NewMockIContext(controller) + + context.EXPECT().ID().Return(t.User.Id, nil) + userSvc.EXPECT().FindOne(t.User.Id).Return(svcResp, nil) + context.EXPECT().JSON(http.StatusOK, expectedResp) + + handler := NewHandler(userSvc, validator) + handler.FindOne(context) +} + +func (t *UserHandlerTest) TestFindOneNotFoundErr() { + controller := gomock.NewController(t.T()) + + userSvc := userMock.NewMockService(controller) + validator := validatorMock.NewMockIDtoValidator(controller) + context := routerMock.NewMockIContext(controller) + + context.EXPECT().ID().Return(t.User.Id, nil) + userSvc.EXPECT().FindOne(t.User.Id).Return(nil, t.NotFoundErr) + context.EXPECT().JSON(http.StatusNotFound, t.NotFoundErr) + + handler := NewHandler(userSvc, validator) + handler.FindOne(context) +} + +func (t *UserHandlerTest) TestFindOneInvalidIDErr() { + controller := gomock.NewController(t.T()) + + userSvc := userMock.NewMockService(controller) + validator := validatorMock.NewMockIDtoValidator(controller) + context := routerMock.NewMockIContext(controller) + + context.EXPECT().ID().Return("", errors.New("Invalid ID")) + context.EXPECT().JSON(http.StatusBadRequest, t.InvalidIDErr) + + handler := NewHandler(userSvc, validator) + handler.FindOne(context) +} + +func (t *UserHandlerTest) TestFindOneInternalErr() { + controller := gomock.NewController(t.T()) + + userSvc := userMock.NewMockService(controller) + validator := validatorMock.NewMockIDtoValidator(controller) + context := routerMock.NewMockIContext(controller) + + context.EXPECT().ID().Return(t.User.Id, nil) + userSvc.EXPECT().FindOne(t.User.Id).Return(nil, t.InternalErr) + context.EXPECT().JSON(http.StatusInternalServerError, t.InternalErr) + + handler := NewHandler(userSvc, validator) + handler.FindOne(context) +} + +func (t *UserHandlerTest) TestFindOneGrpcErr() { + controller := gomock.NewController(t.T()) + + userSvc := userMock.NewMockService(controller) + validator := validatorMock.NewMockIDtoValidator(controller) + context := routerMock.NewMockIContext(controller) + + context.EXPECT().ID().Return(t.User.Id, nil) + userSvc.EXPECT().FindOne(t.User.Id).Return(nil, t.ServiceDownErr) + context.EXPECT().JSON(http.StatusServiceUnavailable, t.ServiceDownErr) + + handler := NewHandler(userSvc, validator) + handler.FindOne(context) +} + +func (t *UserHandlerTest) TestUpdateSuccess() { + svcResp := t.UserDto + expectedResp := t.UserDto + + controller := gomock.NewController(t.T()) + + userSvc := userMock.NewMockService(controller) + validator := validatorMock.NewMockIDtoValidator(controller) + context := routerMock.NewMockIContext(controller) + + context.EXPECT().UserID().Return(t.User.Id) + context.EXPECT().Bind(t.UpdateUserRequest).Return(nil) + validator.EXPECT().Validate(t.UpdateUserRequest).Return(nil) + userSvc.EXPECT().Update(t.User.Id, t.UpdateUserRequest).Return(svcResp, nil) + context.EXPECT().JSON(http.StatusOK, expectedResp) + + handler := NewHandler(userSvc, validator) + handler.Update(context) +} + +func (t *UserHandlerTest) TestUpdateBindErr() { + bindErr := errors.New("Bind err") + expectedResp := &dto.ResponseErr{ + StatusCode: http.StatusBadRequest, + Message: constant.BindingRequestErrorMessage + bindErr.Error(), + Data: nil, + } + + controller := gomock.NewController(t.T()) + + userSvc := userMock.NewMockService(controller) + validator := validatorMock.NewMockIDtoValidator(controller) + context := routerMock.NewMockIContext(controller) + + context.EXPECT().UserID().Return(t.User.Id) + context.EXPECT().Bind(t.UpdateUserRequest).Return(bindErr) + context.EXPECT().JSON(http.StatusBadRequest, expectedResp) + + handler := NewHandler(userSvc, validator) + handler.Update(context) +} + +func (t *UserHandlerTest) TestUpdateValidateErr() { + errorMessage := []string{"First name is required", "Last name is required"} + validateErr := []*dto.BadReqErrResponse{ + { + Message: errorMessage[0], + FailedField: "firstname", + }, + { + Message: errorMessage[1], + FailedField: "lastname", + }, + } + expectedResp := &dto.ResponseErr{ + StatusCode: http.StatusBadRequest, + Message: constant.InvalidRequestBodyMessage + strings.Join(errorMessage, ", "), + Data: nil, + } + + controller := gomock.NewController(t.T()) + + userSvc := userMock.NewMockService(controller) + validator := validatorMock.NewMockIDtoValidator(controller) + context := routerMock.NewMockIContext(controller) + + context.EXPECT().UserID().Return(t.User.Id) + context.EXPECT().Bind(t.UpdateUserRequest).Return(nil) + validator.EXPECT().Validate(t.UpdateUserRequest).Return(validateErr) + context.EXPECT().JSON(http.StatusBadRequest, expectedResp) + + handler := NewHandler(userSvc, validator) + handler.Update(context) +} + +func (t *UserHandlerTest) TestUpdateDuplicateEmailErr() { + expectedResp := &dto.ResponseErr{ + StatusCode: http.StatusConflict, + Message: constant.DuplicateEmailMessage, + Data: nil, + } + + controller := gomock.NewController(t.T()) + + userSvc := userMock.NewMockService(controller) + validator := validatorMock.NewMockIDtoValidator(controller) + context := routerMock.NewMockIContext(controller) + + context.EXPECT().UserID().Return(t.User.Id) + context.EXPECT().Bind(t.UpdateUserRequest).Return(nil) + validator.EXPECT().Validate(t.UpdateUserRequest).Return(nil) + userSvc.EXPECT().Update(t.User.Id, t.UpdateUserRequest).Return(nil, t.DuplicateEmailErr) + context.EXPECT().JSON(http.StatusConflict, expectedResp) + + handler := NewHandler(userSvc, validator) + handler.Update(context) +} + +func (t *UserHandlerTest) TestUpdateInternalErr() { + controller := gomock.NewController(t.T()) + + userSvc := userMock.NewMockService(controller) + validator := validatorMock.NewMockIDtoValidator(controller) + context := routerMock.NewMockIContext(controller) + + context.EXPECT().UserID().Return(t.User.Id) + context.EXPECT().Bind(t.UpdateUserRequest).Return(nil) + validator.EXPECT().Validate(t.UpdateUserRequest).Return(nil) + userSvc.EXPECT().Update(t.User.Id, t.UpdateUserRequest).Return(nil, t.InternalErr) + context.EXPECT().JSON(http.StatusInternalServerError, t.InternalErr) + + handler := NewHandler(userSvc, validator) + handler.Update(context) +} + +func (t *UserHandlerTest) TestUpdateGrpcErr() { + controller := gomock.NewController(t.T()) + + userSvc := userMock.NewMockService(controller) + validator := validatorMock.NewMockIDtoValidator(controller) + context := routerMock.NewMockIContext(controller) + + context.EXPECT().UserID().Return(t.User.Id) + context.EXPECT().Bind(t.UpdateUserRequest).Return(nil) + validator.EXPECT().Validate(t.UpdateUserRequest).Return(nil) + userSvc.EXPECT().Update(t.User.Id, t.UpdateUserRequest).Return(nil, t.ServiceDownErr) + context.EXPECT().JSON(http.StatusServiceUnavailable, t.ServiceDownErr) + + handler := NewHandler(userSvc, validator) + handler.Update(context) +} + +func (t *UserHandlerTest) TestDeleteSuccess() { + deleteResp := &dto.DeleteUserResponse{ + Success: true, + } + expectedResp := deleteResp + + controller := gomock.NewController(t.T()) + + userSvc := userMock.NewMockService(controller) + validator := validatorMock.NewMockIDtoValidator(controller) + context := routerMock.NewMockIContext(controller) + + context.EXPECT().ID().Return(t.User.Id, nil) + userSvc.EXPECT().Delete(t.User.Id).Return(deleteResp, nil) + context.EXPECT().JSON(http.StatusOK, expectedResp) + + handler := NewHandler(userSvc, validator) + handler.Delete(context) +} + +func (t *UserHandlerTest) TestDeleteInvalidIDErr() { + controller := gomock.NewController(t.T()) + + userSvc := userMock.NewMockService(controller) + validator := validatorMock.NewMockIDtoValidator(controller) + context := routerMock.NewMockIContext(controller) + + context.EXPECT().ID().Return("", errors.New("Invalid ID")) + context.EXPECT().JSON(http.StatusBadRequest, t.InvalidIDErr) + + handler := NewHandler(userSvc, validator) + handler.Delete(context) +} + +func (t *UserHandlerTest) TestDeleteInternalErr() { + controller := gomock.NewController(t.T()) + + userSvc := userMock.NewMockService(controller) + validator := validatorMock.NewMockIDtoValidator(controller) + context := routerMock.NewMockIContext(controller) + + context.EXPECT().ID().Return(t.User.Id, nil) + userSvc.EXPECT().Delete(t.User.Id).Return(nil, t.InternalErr) + context.EXPECT().JSON(http.StatusInternalServerError, t.InternalErr) + + handler := NewHandler(userSvc, validator) + handler.Delete(context) +} + +func (t *UserHandlerTest) TestDeleteGrpcErr() { + controller := gomock.NewController(t.T()) + + userSvc := userMock.NewMockService(controller) + validator := validatorMock.NewMockIDtoValidator(controller) + context := routerMock.NewMockIContext(controller) + + context.EXPECT().ID().Return(t.User.Id, nil) + userSvc.EXPECT().Delete(t.User.Id).Return(nil, t.ServiceDownErr) + context.EXPECT().JSON(http.StatusServiceUnavailable, t.ServiceDownErr) + + handler := NewHandler(userSvc, validator) + handler.Delete(context) +} diff --git a/src/app/middleware/auth/auth.middleware.go b/src/app/middleware/auth/auth.middleware.go index 70db9b5..3660164 100644 --- a/src/app/middleware/auth/auth.middleware.go +++ b/src/app/middleware/auth/auth.middleware.go @@ -1,26 +1,30 @@ package auth import ( + "net/http" + "github.com/isd-sgcu/johnjud-gateway/src/app/dto" "github.com/isd-sgcu/johnjud-gateway/src/app/router" "github.com/isd-sgcu/johnjud-gateway/src/app/utils" "github.com/isd-sgcu/johnjud-gateway/src/app/utils/auth" "github.com/isd-sgcu/johnjud-gateway/src/config" + "github.com/isd-sgcu/johnjud-gateway/src/constant/user" authPkg "github.com/isd-sgcu/johnjud-gateway/src/pkg/service/auth" - "net/http" ) type Guard struct { service authPkg.Service excludes map[string]struct{} + adminpath map[string]struct{} conf config.App versionList map[string]struct{} } -func NewAuthGuard(s authPkg.Service, e map[string]struct{}, conf config.App, versionList map[string]struct{}) Guard { +func NewAuthGuard(s authPkg.Service, e map[string]struct{}, a map[string]struct{}, conf config.App, versionList map[string]struct{}) Guard { return Guard{ service: s, excludes: e, + adminpath: a, conf: conf, versionList: versionList, } @@ -29,8 +33,10 @@ func NewAuthGuard(s authPkg.Service, e map[string]struct{}, conf config.App, ver func (m *Guard) Use(ctx router.IContext) error { method := ctx.Method() path := ctx.Path() + path = utils.TrimInList(path, "/", m.versionList) - path = auth.FormatPath(method, path) + ids := auth.FindIDFromPath(path) + path = auth.FormatPath(method, path, ids) if utils.IsExisted(m.excludes, path) { return ctx.Next() } @@ -53,5 +59,14 @@ func (m *Guard) Use(ctx router.IContext) error { ctx.StoreValue("UserId", payload.UserId) ctx.StoreValue("Role", payload.Role) + if utils.IsExisted(m.adminpath, path) && payload.Role != string(user.ADMIN) { + ctx.JSON(http.StatusUnauthorized, dto.ResponseErr{ + StatusCode: http.StatusUnauthorized, + Message: "Limited access", + Data: nil, + }) + return nil + } + return ctx.Next() } diff --git a/src/app/router/context.go b/src/app/router/context.go index 4a1b00f..55e4b1d 100644 --- a/src/app/router/context.go +++ b/src/app/router/context.go @@ -9,6 +9,7 @@ import ( type IContext interface { UserID() string + Role() string Bind(interface{}) error JSON(int, interface{}) ID() (string, error) @@ -18,6 +19,7 @@ type IContext interface { Path() string StoreValue(string, string) Next() error + Queries() map[string]string } type FiberCtx struct { @@ -32,6 +34,10 @@ func (c *FiberCtx) UserID() string { return c.Ctx.Locals("UserId").(string) } +func (c *FiberCtx) Role() string { + return c.Ctx.Locals("Role").(string) +} + func (c *FiberCtx) Bind(v interface{}) error { return c.Ctx.BodyParser(v) } @@ -86,6 +92,10 @@ func (c *FiberCtx) StoreValue(k string, v string) { c.Locals(k, v) } +func (c *FiberCtx) Queries() map[string]string { + return c.Ctx.Queries() +} + //func (c *FiberCtx) Next() { // err := c.Ctx.Next() // fmt.Println(c.Route().Path) diff --git a/src/app/router/image.router.go b/src/app/router/image.router.go index e696190..bebbc0c 100644 --- a/src/app/router/image.router.go +++ b/src/app/router/image.router.go @@ -15,3 +15,10 @@ func (r *FiberRouter) DeleteImage(path string, h func(ctx *FiberCtx)) { return nil }) } + +func (r *FiberRouter) GetImage(path string, h func(ctx *FiberCtx)) { + r.image.Delete(path, func(c *fiber.Ctx) error { + h(NewFiberCtx(c)) + return nil + }) +} diff --git a/src/app/router/like.router.go b/src/app/router/like.router.go index 094d032..08395c4 100644 --- a/src/app/router/like.router.go +++ b/src/app/router/like.router.go @@ -2,21 +2,21 @@ package router import "github.com/gofiber/fiber/v2" -func (r *FiberRouter) GetLike(path string, h func(ctx *FiberCtx)) { +func (r *FiberRouter) GetLike(path string, h func(ctx IContext)) { r.like.Get(path, func(c *fiber.Ctx) error { h(NewFiberCtx(c)) return nil }) } -func (r *FiberRouter) PostLike(path string, h func(ctx *FiberCtx)) { +func (r *FiberRouter) PostLike(path string, h func(ctx IContext)) { r.like.Post(path, func(c *fiber.Ctx) error { h(NewFiberCtx(c)) return nil }) } -func (r *FiberRouter) DeleteLike(path string, h func(ctx *FiberCtx)) { +func (r *FiberRouter) DeleteLike(path string, h func(ctx IContext)) { r.like.Delete(path, func(c *fiber.Ctx) error { h(NewFiberCtx(c)) return nil diff --git a/src/app/router/router.go b/src/app/router/router.go index 0c2716b..0f55c33 100644 --- a/src/app/router/router.go +++ b/src/app/router/router.go @@ -51,8 +51,8 @@ func NewFiberRouter(authGuard IGuard, conf config.App) *FiberRouter { user := GroupWithAuthMiddleware(r, "/user", authGuard.Use) pet := GroupWithAuthMiddleware(r, "/pets", authGuard.Use) - image := GroupWithAuthMiddleware(r, "/image", authGuard.Use) - like := GroupWithAuthMiddleware(r, "/like", authGuard.Use) + image := GroupWithAuthMiddleware(r, "/images", authGuard.Use) + like := GroupWithAuthMiddleware(r, "/likes", authGuard.Use) return &FiberRouter{r, auth, user, pet, image, like} } diff --git a/src/app/router/user.router.go b/src/app/router/user.router.go index f506484..f2da894 100644 --- a/src/app/router/user.router.go +++ b/src/app/router/user.router.go @@ -2,16 +2,23 @@ package router import "github.com/gofiber/fiber/v2" -func (r *FiberRouter) GetUser(path string, h func(ctx *FiberCtx)) { +func (r *FiberRouter) GetUser(path string, h func(ctx IContext)) { r.user.Get(path, func(c *fiber.Ctx) error { h(NewFiberCtx(c)) return nil }) } -func (r *FiberRouter) PutUser(path string, h func(ctx *FiberCtx)) { +func (r *FiberRouter) PutUser(path string, h func(ctx IContext)) { r.user.Put(path, func(c *fiber.Ctx) error { h(NewFiberCtx(c)) return nil }) } + +func (r *FiberRouter) DeleteUser(path string, h func(ctx IContext)) { + r.user.Delete(path, func(c *fiber.Ctx) error { + h(NewFiberCtx(c)) + return nil + }) +} diff --git a/src/app/service/image/image.service.go b/src/app/service/image/image.service.go index 6ff8668..ec00042 100644 --- a/src/app/service/image/image.service.go +++ b/src/app/service/image/image.service.go @@ -1,8 +1,17 @@ package image import ( + "context" + "net/http" + "time" + + "github.com/isd-sgcu/johnjud-gateway/src/app/constant" "github.com/isd-sgcu/johnjud-gateway/src/app/dto" + utils "github.com/isd-sgcu/johnjud-gateway/src/app/utils/image" proto "github.com/isd-sgcu/johnjud-go-proto/johnjud/file/image/v1" + "github.com/rs/zerolog/log" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) type Service struct { @@ -15,14 +24,165 @@ func NewService(client proto.ImageServiceClient) *Service { } } -func (s *Service) FindByPetId(string) ([]*proto.Image, *dto.ResponseErr) { - return nil, nil +func (s *Service) FindByPetId(petId string) ([]*dto.ImageResponse, *dto.ResponseErr) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + res, errRes := s.client.FindByPetId(ctx, &proto.FindImageByPetIdRequest{PetId: petId}) + if errRes != nil { + st, _ := status.FromError(errRes) + log.Error(). + Str("service", "image"). + Str("module", "find by pet id"). + 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.StatusInternalServerError, + Message: constant.InternalErrorMessage, + Data: nil, + } + } + } + return utils.ProtoToDtoList(res.Images), nil } -func (s *Service) Upload(in *dto.ImageDto) (*proto.Image, *dto.ResponseErr) { - return nil, nil +func (s *Service) Upload(in *dto.UploadImageRequest) (*dto.ImageResponse, *dto.ResponseErr) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + request := utils.CreateDtoToProto(in) + res, errRes := s.client.Upload(ctx, request) + if errRes != nil { + st, _ := status.FromError(errRes) + log.Error(). + Err(errRes). + Str("service", "image"). + Str("module", "upload"). + Msg(st.Message()) + switch st.Code() { + case codes.InvalidArgument: + return nil, &dto.ResponseErr{ + StatusCode: http.StatusBadRequest, + Message: constant.InvalidArgumentMessage, + Data: nil, + } + case codes.Unavailable: + return nil, &dto.ResponseErr{ + StatusCode: http.StatusServiceUnavailable, + Message: constant.UnavailableServiceMessage, + Data: nil, + } + default: + return nil, &dto.ResponseErr{ + StatusCode: http.StatusInternalServerError, + Message: constant.InternalErrorMessage, + Data: nil, + } + } + } + return utils.ProtoToDto(res.Image), nil } -func (s *Service) Delete(id string) (bool, *dto.ResponseErr) { - return false, nil +func (s *Service) Delete(id string) (*dto.DeleteImageResponse, *dto.ResponseErr) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + request := &proto.DeleteImageRequest{ + Id: id, + } + + res, errRes := s.client.Delete(ctx, request) + if errRes != nil { + st, _ := status.FromError(errRes) + log.Error(). + Err(errRes). + Str("service", "image"). + Str("module", "delete"). + Msg(st.Message()) + switch st.Code() { + case codes.NotFound: + return nil, &dto.ResponseErr{ + StatusCode: http.StatusNotFound, + Message: constant.ImageNotFoundMessage, + Data: nil, + } + case codes.Unavailable: + return nil, &dto.ResponseErr{ + StatusCode: http.StatusServiceUnavailable, + Message: constant.UnavailableServiceMessage, + Data: nil, + } + default: + return nil, &dto.ResponseErr{ + StatusCode: http.StatusInternalServerError, + Message: constant.InternalErrorMessage, + Data: nil, + } + } + } + return &dto.DeleteImageResponse{ + Success: res.Success, + }, nil +} + +func (s *Service) AssignPet(in *dto.AssignPetRequest) (*dto.AssignPetResponse, *dto.ResponseErr) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + request := &proto.AssignPetRequest{ + Ids: in.Ids, + PetId: in.PetId, + } + + res, errRes := s.client.AssignPet(ctx, request) + if errRes != nil { + st, _ := status.FromError(errRes) + log.Error(). + Err(errRes). + Str("service", "image"). + Str("module", "assign pet"). + Msg(st.Message()) + switch st.Code() { + case codes.InvalidArgument: + return nil, &dto.ResponseErr{ + StatusCode: http.StatusBadRequest, + Message: constant.InvalidArgumentMessage, + Data: nil, + } + 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.StatusInternalServerError, + Message: constant.InternalErrorMessage, + Data: nil, + } + } + } + return &dto.AssignPetResponse{ + Success: res.Success, + }, nil } diff --git a/src/app/service/like/like.service.go b/src/app/service/like/like.service.go index ef610f6..b966ae3 100644 --- a/src/app/service/like/like.service.go +++ b/src/app/service/like/like.service.go @@ -1,28 +1,139 @@ package like import ( + "context" + "net/http" + "time" + + "github.com/isd-sgcu/johnjud-gateway/src/app/constant" "github.com/isd-sgcu/johnjud-gateway/src/app/dto" - proto "github.com/isd-sgcu/johnjud-go-proto/johnjud/backend/like/v1" + utils "github.com/isd-sgcu/johnjud-gateway/src/app/utils/like" + likeProto "github.com/isd-sgcu/johnjud-go-proto/johnjud/backend/like/v1" + "github.com/rs/zerolog/log" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) type Service struct { - client proto.LikeServiceClient + client likeProto.LikeServiceClient } -func NewService(client proto.LikeServiceClient) *Service { +func NewService(client likeProto.LikeServiceClient) *Service { return &Service{ client: client, } } -func (s *Service) FindByUserId(userId string) ([]*proto.Like, *dto.ResponseErr) { - return nil, nil +func (s *Service) FindByUserId(userId string) ([]*dto.LikeResponse, *dto.ResponseErr) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + res, errRes := s.client.FindByUserId(ctx, &likeProto.FindLikeByUserIdRequest{UserId: userId}) + if errRes != nil { + st, _ := status.FromError(errRes) + log.Error(). + Str("service", "like"). + Str("module", "find by user id"). + Msg(st.Message()) + switch st.Code() { + case codes.NotFound: + return nil, &dto.ResponseErr{ + StatusCode: http.StatusNotFound, + Message: constant.UserNotFoundMessage, + Data: nil, + } + case codes.Unavailable: + return nil, &dto.ResponseErr{ + StatusCode: http.StatusServiceUnavailable, + Message: constant.UnavailableServiceMessage, + Data: nil, + } + default: + return nil, &dto.ResponseErr{ + StatusCode: http.StatusInternalServerError, + Message: constant.InternalErrorMessage, + Data: nil, + } + } + } + return utils.ProtoToDtoList(res.Likes), nil } -func (s *Service) Create(in *dto.LikeDto) (*proto.Like, *dto.ResponseErr) { - return nil, nil +func (s *Service) Create(in *dto.CreateLikeRequest) (*dto.LikeResponse, *dto.ResponseErr) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + request := utils.CreateDtoToProto(in) + res, errRes := s.client.Create(ctx, request) + if errRes != nil { + st, _ := status.FromError(errRes) + log.Error(). + Err(errRes). + Str("service", "like"). + Str("module", "create"). + Msg(st.Message()) + switch st.Code() { + case codes.InvalidArgument: + return nil, &dto.ResponseErr{ + StatusCode: http.StatusBadRequest, + Message: constant.InvalidArgumentMessage, + Data: nil, + } + case codes.Unavailable: + return nil, &dto.ResponseErr{ + StatusCode: http.StatusServiceUnavailable, + Message: constant.UnavailableServiceMessage, + Data: nil, + } + default: + return nil, &dto.ResponseErr{ + StatusCode: http.StatusInternalServerError, + Message: constant.InternalErrorMessage, + Data: nil, + } + } + } + return utils.ProtoToDto(res.Like), nil } -func (s *Service) Delete(id string) (bool, *dto.ResponseErr) { - return false, nil +func (s *Service) Delete(id string) (*dto.DeleteLikeResponse, *dto.ResponseErr) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + request := &likeProto.DeleteLikeRequest{ + Id: id, + } + + res, errRes := s.client.Delete(ctx, request) + if errRes != nil { + st, _ := status.FromError(errRes) + log.Error(). + Err(errRes). + Str("service", "like"). + Str("module", "delete"). + Msg(st.Message()) + switch st.Code() { + case codes.NotFound: + return nil, &dto.ResponseErr{ + StatusCode: http.StatusNotFound, + Message: constant.UserNotFoundMessage, + Data: nil, + } + case codes.Unavailable: + return nil, &dto.ResponseErr{ + StatusCode: http.StatusServiceUnavailable, + Message: constant.UnavailableServiceMessage, + Data: nil, + } + default: + return nil, &dto.ResponseErr{ + StatusCode: http.StatusInternalServerError, + Message: constant.InternalErrorMessage, + Data: nil, + } + } + } + return &dto.DeleteLikeResponse{ + Success: res.Success, + }, nil } diff --git a/src/app/service/like/like.service_test.go b/src/app/service/like/like.service_test.go new file mode 100644 index 0000000..c76c74c --- /dev/null +++ b/src/app/service/like/like.service_test.go @@ -0,0 +1,315 @@ +package like + +import ( + "net/http" + "testing" + + "github.com/bxcodec/faker/v4" + "github.com/isd-sgcu/johnjud-gateway/src/app/constant" + "github.com/isd-sgcu/johnjud-gateway/src/app/dto" + utils "github.com/isd-sgcu/johnjud-gateway/src/app/utils/like" + likeMock "github.com/isd-sgcu/johnjud-gateway/src/mocks/client/like" + likeProto "github.com/isd-sgcu/johnjud-go-proto/johnjud/backend/like/v1" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +type LikeServiceTest struct { + suite.Suite + Likes []*likeProto.Like + Like *likeProto.Like + LikeResponse *dto.LikeResponse + CreateLikeProtoReq *likeProto.CreateLikeRequest + CreateLikeDtoReq *dto.CreateLikeRequest + DeleteLikeProtoReq *likeProto.DeleteLikeRequest + DeleteLikeDtoReq *dto.DeleteLikeRequest + NotFoundErr *dto.ResponseErr + + UnavailableServiceErr *dto.ResponseErr + InvalidArgumentErr *dto.ResponseErr + InternalErr *dto.ResponseErr +} + +func TestLikeService(t *testing.T) { + suite.Run(t, new(LikeServiceTest)) +} + +func (t *LikeServiceTest) SetupTest() { + var likes []*likeProto.Like + for i := 0; i <= 3; i++ { + like := &likeProto.Like{ + Id: faker.UUIDDigit(), + UserId: faker.UUIDDigit(), + PetId: faker.UUIDDigit(), + } + likes = append(likes, like) + } + + t.Likes = likes + t.Like = likes[0] + + t.CreateLikeProtoReq = &likeProto.CreateLikeRequest{ + Like: &likeProto.Like{ + UserId: t.Like.UserId, + PetId: t.Like.PetId, + }, + } + + t.CreateLikeDtoReq = &dto.CreateLikeRequest{ + UserID: t.Like.UserId, + PetID: t.Like.PetId, + } + + t.DeleteLikeProtoReq = &likeProto.DeleteLikeRequest{ + Id: t.Like.Id, + } + + t.UnavailableServiceErr = &dto.ResponseErr{ + StatusCode: http.StatusServiceUnavailable, + Message: constant.UnavailableServiceMessage, + Data: nil, + } + + t.NotFoundErr = &dto.ResponseErr{ + StatusCode: http.StatusNotFound, + Message: constant.UserNotFoundMessage, + Data: nil, + } + + t.InternalErr = &dto.ResponseErr{ + StatusCode: http.StatusInternalServerError, + Message: constant.InternalErrorMessage, + Data: nil, + } + + t.InvalidArgumentErr = &dto.ResponseErr{ + StatusCode: http.StatusBadRequest, + Message: constant.InvalidArgumentMessage, + Data: nil, + } +} + +func (t *LikeServiceTest) TestFindByUserIdSuccess() { + protoReq := &likeProto.FindLikeByUserIdRequest{ + UserId: t.Like.UserId, + } + protoResp := &likeProto.FindLikeByUserIdResponse{ + Likes: t.Likes, + } + + expected := utils.ProtoToDtoList(t.Likes) + + client := likeMock.LikeClientMock{} + client.On("FindByUserId", protoReq).Return(protoResp, nil) + + svc := NewService(&client) + actual, err := svc.FindByUserId(t.Like.UserId) + + assert.Nil(t.T(), err) + assert.Equal(t.T(), expected, actual) +} + +func (t *LikeServiceTest) TestFindByUserIdNotFoundError() { + protoReq := &likeProto.FindLikeByUserIdRequest{ + UserId: t.Like.UserId, + } + + clientErr := status.Error(codes.NotFound, constant.UserNotFoundMessage) + + expected := t.NotFoundErr + + client := likeMock.LikeClientMock{} + client.On("FindByUserId", protoReq).Return(nil, clientErr) + + svc := NewService(&client) + actual, err := svc.FindByUserId(t.Like.UserId) + + assert.Nil(t.T(), actual) + assert.Equal(t.T(), expected, err) +} + +func (t *LikeServiceTest) TestFindByUserIdUnavailableServiceError() { + protoReq := &likeProto.FindLikeByUserIdRequest{ + UserId: t.Like.UserId, + } + + clientErr := status.Error(codes.Unavailable, constant.UnavailableServiceMessage) + + expected := t.UnavailableServiceErr + + client := likeMock.LikeClientMock{} + client.On("FindByUserId", protoReq).Return(nil, clientErr) + + svc := NewService(&client) + actual, err := svc.FindByUserId(t.Like.UserId) + + assert.Nil(t.T(), actual) + assert.Equal(t.T(), expected, err) +} + +func (t *LikeServiceTest) TestFindByUserIdInternalError() { + protoReq := &likeProto.FindLikeByUserIdRequest{ + UserId: t.Like.UserId, + } + + clientErr := status.Error(codes.Internal, constant.InternalErrorMessage) + + expected := t.InternalErr + + client := likeMock.LikeClientMock{} + client.On("FindByUserId", protoReq).Return(nil, clientErr) + + svc := NewService(&client) + actual, err := svc.FindByUserId(t.Like.UserId) + + assert.Nil(t.T(), actual) + assert.Equal(t.T(), expected, err) +} + +func (t *LikeServiceTest) TestCreateSuccess() { + protoReq := t.CreateLikeProtoReq + protoResp := &likeProto.CreateLikeResponse{ + Like: t.Like, + } + + expected := utils.ProtoToDto(t.Like) + + client := &likeMock.LikeClientMock{} + client.On("Create", protoReq).Return(protoResp, nil) + + svc := NewService(client) + actual, err := svc.Create(t.CreateLikeDtoReq) + + assert.Nil(t.T(), err) + assert.Equal(t.T(), expected, actual) +} + +func (t *LikeServiceTest) TestCreateInvalidArgumentError() { + protoReq := t.CreateLikeProtoReq + + expected := t.InvalidArgumentErr + + clientErr := status.Error(codes.InvalidArgument, constant.InvalidArgumentMessage) + + client := &likeMock.LikeClientMock{} + client.On("Create", protoReq).Return(nil, clientErr) + + svc := NewService(client) + actual, err := svc.Create(t.CreateLikeDtoReq) + + assert.Nil(t.T(), actual) + assert.Equal(t.T(), expected, err) +} + +func (t *LikeServiceTest) TestCreateInternalError() { + protoReq := t.CreateLikeProtoReq + + expected := t.InvalidArgumentErr + + clientErr := status.Error(codes.InvalidArgument, constant.InvalidArgumentMessage) + + client := &likeMock.LikeClientMock{} + client.On("Create", protoReq).Return(nil, clientErr) + + svc := NewService(client) + actual, err := svc.Create(t.CreateLikeDtoReq) + + assert.Nil(t.T(), actual) + assert.Equal(t.T(), expected, err) +} + +func (t *LikeServiceTest) TestCreateUnavailableServiceError() { + protoReq := t.CreateLikeProtoReq + + expected := t.UnavailableServiceErr + + clientErr := status.Error(codes.Unavailable, constant.UnavailableServiceMessage) + + client := &likeMock.LikeClientMock{} + client.On("Create", protoReq).Return(nil, clientErr) + + svc := NewService(client) + actual, err := svc.Create(t.CreateLikeDtoReq) + + assert.Nil(t.T(), actual) + assert.Equal(t.T(), expected, err) +} + +func (t *LikeServiceTest) TestDeleteSuccess() { + protoReq := &likeProto.DeleteLikeRequest{ + Id: t.Like.Id, + } + protoResp := &likeProto.DeleteLikeResponse{ + Success: true, + } + + expected := &dto.DeleteLikeResponse{Success: true} + + client := &likeMock.LikeClientMock{} + client.On("Delete", protoReq).Return(protoResp, nil) + + svc := NewService(client) + actual, err := svc.Delete(t.Like.Id) + + assert.Nil(t.T(), err) + assert.Equal(t.T(), expected, actual) +} + +func (t *LikeServiceTest) TestDeleteNotFoundError() { + protoReq := &likeProto.DeleteLikeRequest{ + Id: t.Like.Id, + } + + clientErr := status.Error(codes.NotFound, constant.UserNotFoundMessage) + + expected := t.NotFoundErr + + client := &likeMock.LikeClientMock{} + client.On("Delete", protoReq).Return(nil, clientErr) + + svc := NewService(client) + actual, err := svc.Delete(t.Like.Id) + + assert.Nil(t.T(), actual) + assert.Equal(t.T(), expected, err) +} + +func (t *LikeServiceTest) TestDeleteUnavailableServiceError() { + protoReq := &likeProto.DeleteLikeRequest{ + Id: t.Like.Id, + } + + clientErr := status.Error(codes.Unavailable, constant.UnavailableServiceMessage) + + expected := t.UnavailableServiceErr + + client := &likeMock.LikeClientMock{} + client.On("Delete", protoReq).Return(nil, clientErr) + + svc := NewService(client) + actual, err := svc.Delete(t.Like.Id) + + assert.Nil(t.T(), actual) + assert.Equal(t.T(), expected, err) +} + +func (t *LikeServiceTest) TestDeleteInternalError() { + protoReq := &likeProto.DeleteLikeRequest{ + Id: t.Like.Id, + } + + clientErr := status.Error(codes.Internal, constant.InternalErrorMessage) + + expected := t.InternalErr + + client := &likeMock.LikeClientMock{} + client.On("Delete", protoReq).Return(nil, clientErr) + + svc := NewService(client) + actual, err := svc.Delete(t.Like.Id) + + assert.Nil(t.T(), actual) + assert.Equal(t.T(), expected, err) +} diff --git a/src/app/service/pet/pet.service.go b/src/app/service/pet/pet.service.go index 147b22c..a18a682 100644 --- a/src/app/service/pet/pet.service.go +++ b/src/app/service/pet/pet.service.go @@ -24,11 +24,11 @@ func NewService(petClient petproto.PetServiceClient) *Service { } } -func (s *Service) FindAll() (result []*dto.PetResponse, err *dto.ResponseErr) { +func (s *Service) FindAll(in *dto.FindAllPetRequest) (result *dto.FindAllPetResponse, err *dto.ResponseErr) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() - res, errRes := s.petClient.FindAll(ctx, &petproto.FindAllPetRequest{}) + res, errRes := s.petClient.FindAll(ctx, utils.FindAllDtoToProto(in)) if errRes != nil { st, _ := status.FromError(errRes) log.Error(). @@ -51,8 +51,13 @@ func (s *Service) FindAll() (result []*dto.PetResponse, err *dto.ResponseErr) { } } imagesList := utils.MockImageList(len(res.Pets)) - findAllResponse := utils.RawToDtoList(res.Pets, imagesList) - return findAllResponse, nil + findAllDto := utils.ProtoToDtoList(res.Pets, imagesList) + metaData := utils.MetadataProtoToDto(res.Metadata) + + return &dto.FindAllPetResponse{ + Pets: findAllDto, + Metadata: metaData, + }, nil } func (s *Service) FindOne(id string) (result *dto.PetResponse, err *dto.ResponseErr) { @@ -94,7 +99,7 @@ func (s *Service) FindOne(id string) (result *dto.PetResponse, err *dto.Response return findOneResponse, nil } -func (s *Service) Create(in *dto.CreatePetRequest) (ressult *dto.PetResponse, err *dto.ResponseErr) { +func (s *Service) Create(in *dto.CreatePetRequest) (result *dto.PetResponse, err *dto.ResponseErr) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() @@ -112,7 +117,7 @@ func (s *Service) Create(in *dto.CreatePetRequest) (ressult *dto.PetResponse, er case codes.InvalidArgument: return nil, &dto.ResponseErr{ StatusCode: http.StatusBadRequest, - Message: constant.InvalidArgument, + Message: constant.InvalidArgumentMessage, Data: nil, } case codes.Unavailable: @@ -158,7 +163,7 @@ func (s *Service) Update(id string, in *dto.UpdatePetRequest) (result *dto.PetRe case codes.InvalidArgument: return nil, &dto.ResponseErr{ StatusCode: http.StatusBadRequest, - Message: constant.InvalidArgument, + Message: constant.InvalidArgumentMessage, Data: nil, } case codes.Unavailable: @@ -196,29 +201,24 @@ func (s *Service) Delete(id string) (result *dto.DeleteResponse, err *dto.Respon Msg(st.Message()) switch st.Code() { case codes.NotFound: - return &dto.DeleteResponse{ - Success: false, - }, &dto.ResponseErr{ - StatusCode: http.StatusNotFound, - Message: constant.PetNotFoundMessage, - Data: nil, - } + return nil, &dto.ResponseErr{ + StatusCode: http.StatusNotFound, + Message: constant.PetNotFoundMessage, + Data: nil, + } case codes.Unavailable: - return &dto.DeleteResponse{ - Success: false, - }, &dto.ResponseErr{ - StatusCode: http.StatusServiceUnavailable, - Message: constant.UnavailableServiceMessage, - Data: nil, - } - } - return &dto.DeleteResponse{ - Success: false, - }, &dto.ResponseErr{ + return nil, &dto.ResponseErr{ + StatusCode: http.StatusServiceUnavailable, + Message: constant.UnavailableServiceMessage, + Data: nil, + } + default: + return nil, &dto.ResponseErr{ StatusCode: http.StatusInternalServerError, Message: constant.InternalErrorMessage, Data: nil, } + } } return &dto.DeleteResponse{ Success: res.Success, @@ -271,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 +} diff --git a/src/app/service/pet/pet.service_test.go b/src/app/service/pet/pet.service_test.go index abab448..909b39d 100644 --- a/src/app/service/pet/pet.service_test.go +++ b/src/app/service/pet/pet.service_test.go @@ -23,13 +23,17 @@ type PetServiceTest struct { suite.Suite Pets []*petproto.Pet Pet *petproto.Pet + MetadataDto *dto.FindAllMetadata + MetadataProto *petproto.FindAllPetMetaData PetNotVisible *petproto.Pet + FindAllPetReq *petproto.FindAllPetRequest UpdatePetReq *petproto.UpdatePetRequest CreatePetReq *petproto.CreatePetRequest ChangeViewPetReq *petproto.ChangeViewPetRequest DeletePetReq *petproto.DeletePetRequest AdoptReq *petproto.AdoptPetRequest PetDto *dto.PetResponse + FindAllPetDto *dto.FindAllPetRequest CreatePetDto *dto.CreatePetRequest UpdatePetDto *dto.UpdatePetRequest NotFoundErr *dto.ResponseErr @@ -37,6 +41,7 @@ type PetServiceTest struct { InvalidArgumentErr *dto.ResponseErr InternalErr *dto.ResponseErr ChangeViewedPetDto *dto.ChangeViewPetRequest + AdoptDto *dto.AdoptByRequest Images []*imgproto.Image ImagesList [][]*imgproto.Image @@ -50,25 +55,27 @@ func (t *PetServiceTest) SetupTest() { imagesList := utils.MockImageList(3) t.ImagesList = imagesList t.Images = imagesList[0] + genders := []pet.Gender{pet.MALE, pet.FEMALE} + statuses := []pet.Status{pet.ADOPTED, pet.FINDHOME} var pets []*petproto.Pet for i := 0; i <= 3; i++ { pet := &petproto.Pet{ Id: faker.UUIDDigit(), Type: faker.Word(), - Species: faker.Word(), Name: faker.Name(), Birthdate: faker.Word(), - Gender: petproto.Gender(rand.Intn(1) + 1), + Gender: string(genders[rand.Intn(2)]), + Color: faker.Word(), + Pattern: faker.Word(), Habit: faker.Paragraph(), Caption: faker.Paragraph(), Images: imagesList[i], - Status: petproto.PetStatus(rand.Intn(1) + 1), + Status: string(statuses[rand.Intn(2)]), IsSterile: true, IsVaccinated: true, IsVisible: true, - IsClubPet: true, - Background: faker.Paragraph(), + Origin: faker.Paragraph(), Address: faker.Paragraph(), Contact: faker.Paragraph(), AdoptBy: faker.UUIDDigit(), @@ -77,16 +84,31 @@ func (t *PetServiceTest) SetupTest() { pets = append(pets, pet) } + t.MetadataDto = &dto.FindAllMetadata{ + Page: 1, + TotalPages: 1, + PageSize: len(t.Pets), + Total: len(t.Pets), + } + + t.MetadataProto = &petproto.FindAllPetMetaData{ + Page: 1, + TotalPages: 1, + PageSize: int32(len(t.Pets)), + Total: int32(len(t.Pets)), + } + t.Pets = pets t.Pet = t.Pets[0] t.PetNotVisible = &petproto.Pet{ Id: t.Pet.Id, Type: t.Pet.Type, - Species: t.Pet.Species, Name: t.Pet.Name, Birthdate: t.Pet.Birthdate, Gender: t.Pet.Gender, + Color: t.Pet.Color, + Pattern: t.Pet.Pattern, Habit: t.Pet.Habit, Caption: t.Pet.Caption, Images: t.Pet.Images, @@ -94,8 +116,7 @@ func (t *PetServiceTest) SetupTest() { IsSterile: t.Pet.IsSterile, IsVaccinated: t.Pet.IsVaccinated, IsVisible: false, - IsClubPet: t.Pet.IsClubPet, - Background: t.Pet.Background, + Origin: t.Pet.Origin, Address: t.Pet.Address, Contact: t.Pet.Contact, AdoptBy: t.Pet.AdoptBy, @@ -103,12 +124,25 @@ func (t *PetServiceTest) SetupTest() { t.PetDto = utils.ProtoToDto(t.Pet, t.Pet.Images) + t.FindAllPetDto = &dto.FindAllPetRequest{ + Search: "", + Type: "", + Gender: "", + Color: "", + Pattern: "", + Age: "", + Origin: "", + PageSize: len(t.Pets), + Page: 1, + } + t.CreatePetDto = &dto.CreatePetRequest{ Type: t.Pet.Type, - Species: t.Pet.Species, Name: t.Pet.Name, Birthdate: t.Pet.Birthdate, Gender: pet.Gender(t.Pet.Gender), + Color: t.Pet.Color, + Pattern: t.Pet.Pattern, Habit: t.Pet.Habit, Caption: t.Pet.Caption, Images: []string{}, @@ -116,8 +150,7 @@ func (t *PetServiceTest) SetupTest() { IsSterile: &t.Pet.IsSterile, IsVaccinated: &t.Pet.IsVaccinated, IsVisible: &t.Pet.IsVisible, - IsClubPet: &t.Pet.IsClubPet, - Background: t.Pet.Background, + Origin: t.Pet.Origin, Address: t.Pet.Address, Contact: t.Pet.Contact, AdoptBy: t.Pet.AdoptBy, @@ -125,10 +158,11 @@ func (t *PetServiceTest) SetupTest() { t.UpdatePetDto = &dto.UpdatePetRequest{ Type: t.Pet.Type, - Species: t.Pet.Species, Name: t.Pet.Name, Birthdate: t.Pet.Birthdate, Gender: pet.Gender(t.Pet.Gender), + Color: t.Pet.Color, + Pattern: t.Pet.Pattern, Habit: t.Pet.Habit, Caption: t.Pet.Caption, Images: []string{}, @@ -136,13 +170,13 @@ func (t *PetServiceTest) SetupTest() { IsSterile: &t.Pet.IsSterile, IsVaccinated: &t.Pet.IsVaccinated, IsVisible: &t.Pet.IsVisible, - IsClubPet: &t.Pet.IsClubPet, - Background: t.Pet.Background, + Origin: t.Pet.Origin, Address: t.Pet.Address, Contact: t.Pet.Contact, AdoptBy: t.Pet.AdoptBy, } + t.FindAllPetReq = utils.FindAllDtoToProto(t.FindAllPetDto) t.CreatePetReq = utils.CreateDtoToProto(t.CreatePetDto) t.UpdatePetReq = utils.UpdateDtoToProto(t.Pet.Id, t.UpdatePetDto) @@ -160,6 +194,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, @@ -180,41 +219,45 @@ func (t *PetServiceTest) SetupTest() { t.InvalidArgumentErr = &dto.ResponseErr{ StatusCode: http.StatusBadRequest, - Message: constant.InvalidArgument, + Message: constant.InvalidArgumentMessage, Data: nil, } } func (t *PetServiceTest) TestFindAllSuccess() { - protoReq := &petproto.FindAllPetRequest{} protoResp := &petproto.FindAllPetResponse{ - Pets: t.Pets, + Pets: t.Pets, + Metadata: t.MetadataProto, } - expected := utils.RawToDtoList(t.Pets, t.ImagesList) + findAllPPetsDto := utils.ProtoToDtoList(t.Pets, t.ImagesList) + metadataDto := t.MetadataDto + + expected := &dto.FindAllPetResponse{ + Pets: findAllPPetsDto, + Metadata: metadataDto, + } client := petmock.PetClientMock{} - client.On("FindAll", protoReq).Return(protoResp, nil) + client.On("FindAll", t.FindAllPetReq).Return(protoResp, nil) svc := NewService(&client) - actual, err := svc.FindAll() + actual, err := svc.FindAll(t.FindAllPetDto) assert.Nil(t.T(), err) assert.Equal(t.T(), expected, actual) } func (t *PetServiceTest) TestFindAllUnavailableServiceError() { - protoReq := &petproto.FindAllPetRequest{} - expected := t.UnavailableServiceErr clientErr := status.Error(codes.Unavailable, constant.UnavailableServiceMessage) client := petmock.PetClientMock{} - client.On("FindAll", protoReq).Return(nil, clientErr) + client.On("FindAll", t.FindAllPetReq).Return(nil, clientErr) svc := NewService(&client) - actual, err := svc.FindAll() + actual, err := svc.FindAll(t.FindAllPetDto) assert.Nil(t.T(), actual) assert.Equal(t.T(), expected, err) @@ -301,7 +344,7 @@ func (t *PetServiceTest) TestCreateInvalidArgumentError() { expected := t.InvalidArgumentErr - clientErr := status.Error(codes.InvalidArgument, constant.InvalidArgument) + clientErr := status.Error(codes.InvalidArgument, constant.InvalidArgumentMessage) client := &petmock.PetClientMock{} client.On("Create", protoReq).Return(nil, clientErr) @@ -434,7 +477,7 @@ func (t *PetServiceTest) TestDeleteNotFound() { svc := NewService(client) actual, err := svc.Delete(t.Pet.Id) - assert.Equal(t.T(), &dto.DeleteResponse{Success: false}, actual) + assert.Nil(t.T(), actual) assert.Equal(t.T(), expected, err) } @@ -455,7 +498,7 @@ func (t *PetServiceTest) TestDeleteServiceUnavailableError() { svc := NewService(client) actual, err := svc.Delete(t.Pet.Id) - assert.Equal(t.T(), &dto.DeleteResponse{Success: false}, actual) + assert.Nil(t.T(), actual) assert.Equal(t.T(), expected, err) } @@ -514,3 +557,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) +} diff --git a/src/app/service/user/user.service.go b/src/app/service/user/user.service.go index 382bb3f..d4186f2 100644 --- a/src/app/service/user/user.service.go +++ b/src/app/service/user/user.service.go @@ -1,8 +1,16 @@ package user import ( + "context" + "net/http" + "time" + + "github.com/isd-sgcu/johnjud-gateway/src/app/constant" "github.com/isd-sgcu/johnjud-gateway/src/app/dto" proto "github.com/isd-sgcu/johnjud-go-proto/johnjud/auth/user/v1" + "github.com/rs/zerolog/log" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) type Service struct { @@ -15,10 +23,131 @@ func NewService(client proto.UserServiceClient) *Service { } } -func (s *Service) FindOne(id string) (*proto.User, *dto.ResponseErr) { - return nil, nil +func (s *Service) FindOne(id string) (*dto.User, *dto.ResponseErr) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + response, err := s.client.FindOne(ctx, &proto.FindOneUserRequest{ + Id: id, + }) + if err != nil { + st, _ := status.FromError(err) + log.Error(). + Err(err). + Str("service", "user"). + Str("module", "find one"). + Msg(st.Message()) + switch st.Code() { + case codes.NotFound: + return nil, &dto.ResponseErr{ + StatusCode: http.StatusNotFound, + Message: constant.UserNotFoundMessage, + Data: nil, + } + + case codes.Unavailable: + return nil, &dto.ResponseErr{ + StatusCode: http.StatusServiceUnavailable, + Message: constant.UnavailableServiceMessage, + Data: nil, + } + default: + return nil, &dto.ResponseErr{ + StatusCode: http.StatusInternalServerError, + Message: constant.InternalErrorMessage, + Data: nil, + } + } + } + + return &dto.User{ + Id: response.User.Id, + Firstname: response.User.Firstname, + Lastname: response.User.Lastname, + Email: response.User.Email, + }, nil +} + +func (s *Service) Update(id string, in *dto.UpdateUserRequest) (*dto.User, *dto.ResponseErr) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + response, err := s.client.Update(ctx, &proto.UpdateUserRequest{ + Id: id, + Firstname: in.Firstname, + Lastname: in.Lastname, + Password: in.Password, + Email: in.Email, + }) + if err != nil { + st, _ := status.FromError(err) + log.Error(). + Err(err). + Str("service", "user"). + Str("module", "update"). + Msg(st.Message()) + switch st.Code() { + case codes.AlreadyExists: + return nil, &dto.ResponseErr{ + StatusCode: http.StatusConflict, + Message: constant.DuplicateEmailMessage, + Data: nil, + } + + case codes.Unavailable: + return nil, &dto.ResponseErr{ + StatusCode: http.StatusServiceUnavailable, + Message: constant.UnavailableServiceMessage, + Data: nil, + } + default: + return nil, &dto.ResponseErr{ + StatusCode: http.StatusInternalServerError, + Message: constant.InternalErrorMessage, + Data: nil, + } + } + } + + return &dto.User{ + Id: response.User.Id, + Firstname: response.User.Firstname, + Lastname: response.User.Lastname, + Email: response.User.Email, + }, nil } -func (s *Service) Update(id string, in *dto.UpdateUserDto) (*proto.User, *dto.ResponseErr) { - return nil, nil +func (s *Service) Delete(id string) (*dto.DeleteUserResponse, *dto.ResponseErr) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + response, err := s.client.Delete(ctx, &proto.DeleteUserRequest{ + Id: id, + }) + if err != nil { + st, _ := status.FromError(err) + log.Error(). + Err(err). + Str("service", "user"). + Str("module", "delete"). + Msg(st.Message()) + switch st.Code() { + case codes.Unavailable: + return nil, &dto.ResponseErr{ + StatusCode: http.StatusServiceUnavailable, + Message: constant.UnavailableServiceMessage, + Data: nil, + } + default: + return nil, &dto.ResponseErr{ + StatusCode: http.StatusInternalServerError, + Message: constant.InternalErrorMessage, + Data: nil, + } + } + } + + return &dto.DeleteUserResponse{ + Success: response.Success, + }, nil } diff --git a/src/app/service/user/user.service_test.go b/src/app/service/user/user.service_test.go new file mode 100644 index 0000000..9630508 --- /dev/null +++ b/src/app/service/user/user.service_test.go @@ -0,0 +1,278 @@ +package user + +import ( + "net/http" + "testing" + + "github.com/go-faker/faker/v4" + "github.com/isd-sgcu/johnjud-gateway/src/app/constant" + "github.com/isd-sgcu/johnjud-gateway/src/app/dto" + "github.com/isd-sgcu/johnjud-gateway/src/mocks/client/user" + proto "github.com/isd-sgcu/johnjud-go-proto/johnjud/auth/user/v1" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +type UserServiceTest struct { + suite.Suite + User *proto.User + FindOneUserReq *proto.FindOneUserRequest + UpdateUserReq *proto.UpdateUserRequest + UpdateUserDto *dto.UpdateUserRequest + DeleteUserReq *proto.DeleteUserRequest + NotFoundErr *dto.ResponseErr + UnavailableServiceErr *dto.ResponseErr + ConflictErr *dto.ResponseErr + InternalErr *dto.ResponseErr +} + +func TestUserService(t *testing.T) { + suite.Run(t, new(UserServiceTest)) +} + +func (t *UserServiceTest) SetupTest() { + t.User = &proto.User{ + Id: faker.UUIDDigit(), + Email: faker.Email(), + Password: faker.Password(), + Firstname: faker.FirstName(), + Lastname: faker.LastName(), + Role: "user", + } + + t.FindOneUserReq = &proto.FindOneUserRequest{ + Id: t.User.Id, + } + + t.UpdateUserDto = &dto.UpdateUserRequest{ + Email: faker.Email(), + Password: faker.Password(), + Firstname: faker.FirstName(), + Lastname: faker.LastName(), + } + + t.UpdateUserReq = &proto.UpdateUserRequest{ + Id: t.User.Id, + Email: t.UpdateUserDto.Email, + Password: t.UpdateUserDto.Password, + Firstname: t.UpdateUserDto.Firstname, + Lastname: t.UpdateUserDto.Lastname, + } + + t.DeleteUserReq = &proto.DeleteUserRequest{ + Id: t.User.Id, + } + + t.UnavailableServiceErr = &dto.ResponseErr{ + StatusCode: http.StatusServiceUnavailable, + Message: constant.UnavailableServiceMessage, + Data: nil, + } + + t.NotFoundErr = &dto.ResponseErr{ + StatusCode: http.StatusNotFound, + Message: constant.UserNotFoundMessage, + Data: nil, + } + + t.ConflictErr = &dto.ResponseErr{ + StatusCode: http.StatusConflict, + Message: constant.DuplicateEmailMessage, + Data: nil, + } + + t.InternalErr = &dto.ResponseErr{ + StatusCode: http.StatusInternalServerError, + Message: constant.InternalErrorMessage, + Data: nil, + } +} + +func (t *UserServiceTest) TestFindOneSuccess() { + protoResp := &proto.FindOneUserResponse{ + User: &proto.User{ + Id: t.User.Id, + Email: t.User.Email, + Firstname: t.User.Firstname, + Lastname: t.User.Lastname, + Role: t.User.Role, + }, + } + + expected := &dto.User{ + Id: t.User.Id, + Email: t.User.Email, + Firstname: t.User.Firstname, + Lastname: t.User.Lastname, + } + + client := user.UserClientMock{} + client.On("FindOne", t.FindOneUserReq).Return(protoResp, nil) + + svc := NewService(&client) + actual, err := svc.FindOne(t.User.Id) + + assert.Nil(t.T(), err) + assert.Equal(t.T(), expected, actual) +} + +func (t *UserServiceTest) TestFindOneNotFoundError() { + expected := t.NotFoundErr + + client := user.UserClientMock{} + clienErr := status.Error(codes.NotFound, constant.UserNotFoundMessage) + client.On("FindOne", t.FindOneUserReq).Return(nil, clienErr) + + svc := NewService(&client) + actual, err := svc.FindOne(t.User.Id) + + assert.Nil(t.T(), actual) + assert.Equal(t.T(), expected, err) +} + +func (t *UserServiceTest) TestFindOneUnavailableServiceError() { + expected := t.UnavailableServiceErr + + client := user.UserClientMock{} + clientErr := status.Error(codes.Unavailable, constant.UnavailableServiceMessage) + client.On("FindOne", t.FindOneUserReq).Return(nil, clientErr) + + svc := NewService(&client) + actual, err := svc.FindOne(t.User.Id) + + assert.Nil(t.T(), actual) + assert.Equal(t.T(), expected, err) +} + +func (t *UserServiceTest) TestFindOneInternalError() { + expected := t.InternalErr + + client := user.UserClientMock{} + clientErr := status.Error(codes.Internal, constant.InternalErrorMessage) + client.On("FindOne", t.FindOneUserReq).Return(nil, clientErr) + + svc := NewService(&client) + actual, err := svc.FindOne(t.User.Id) + + assert.Nil(t.T(), actual) + assert.Equal(t.T(), expected, err) +} + +func (t *UserServiceTest) TestUpdateSuccess() { + protoResp := &proto.UpdateUserResponse{ + User: &proto.User{ + Id: t.User.Id, + Email: t.User.Email, + Firstname: t.User.Firstname, + Lastname: t.User.Lastname, + Role: t.User.Role, + }, + } + + expected := &dto.User{ + Id: t.User.Id, + Email: t.User.Email, + Firstname: t.User.Firstname, + Lastname: t.User.Lastname, + } + + client := user.UserClientMock{} + client.On("Update", t.UpdateUserReq).Return(protoResp, nil) + + svc := NewService(&client) + actual, err := svc.Update(t.User.Id, t.UpdateUserDto) + + assert.Nil(t.T(), err) + assert.Equal(t.T(), expected, actual) +} + +func (t *UserServiceTest) TestUpdateDuplicateEmail() { + expected := t.ConflictErr + + client := user.UserClientMock{} + clientErr := status.Error(codes.AlreadyExists, constant.DuplicateEmailMessage) + client.On("Update", t.UpdateUserReq).Return(nil, clientErr) + + svc := NewService(&client) + actual, err := svc.Update(t.User.Id, t.UpdateUserDto) + + assert.Nil(t.T(), actual) + assert.Equal(t.T(), expected, err) +} + +func (t *UserServiceTest) TestUpdateUnavailableServiceError() { + expected := t.UnavailableServiceErr + + client := user.UserClientMock{} + clientErr := status.Error(codes.Unavailable, constant.UnavailableServiceMessage) + client.On("Update", t.UpdateUserReq).Return(nil, clientErr) + + svc := NewService(&client) + actual, err := svc.Update(t.User.Id, t.UpdateUserDto) + + assert.Nil(t.T(), actual) + assert.Equal(t.T(), expected, err) +} + +func (t *UserServiceTest) TestUpdateInternalError() { + expected := t.InternalErr + + client := user.UserClientMock{} + clientErr := status.Error(codes.Internal, constant.InternalErrorMessage) + client.On("Update", t.UpdateUserReq).Return(nil, clientErr) + + svc := NewService(&client) + actual, err := svc.Update(t.User.Id, t.UpdateUserDto) + + assert.Nil(t.T(), actual) + assert.Equal(t.T(), expected, err) +} + +func (t *UserServiceTest) TestDeleteSuccess() { + protoResp := &proto.DeleteUserResponse{ + Success: true, + } + + expected := &dto.DeleteUserResponse{ + Success: true, + } + + client := user.UserClientMock{} + client.On("Delete", t.DeleteUserReq).Return(protoResp, nil) + + svc := NewService(&client) + actual, err := svc.Delete(t.User.Id) + + assert.Nil(t.T(), err) + assert.Equal(t.T(), expected, actual) +} + +func (t *UserServiceTest) TestDeleteUnavailableServiceError() { + expected := t.UnavailableServiceErr + + client := user.UserClientMock{} + clientErr := status.Error(codes.Unavailable, constant.UnavailableServiceMessage) + client.On("Delete", t.DeleteUserReq).Return(nil, clientErr) + + svc := NewService(&client) + actual, err := svc.Delete(t.User.Id) + + assert.Nil(t.T(), actual) + assert.Equal(t.T(), expected, err) +} + +func (t *UserServiceTest) TestDeleteInternalError() { + expected := t.InternalErr + + client := user.UserClientMock{} + clientErr := status.Error(codes.Internal, constant.InternalErrorMessage) + client.On("Delete", t.DeleteUserReq).Return(nil, clientErr) + + svc := NewService(&client) + actual, err := svc.Delete(t.User.Id) + + assert.Nil(t.T(), actual) + assert.Equal(t.T(), expected, err) +} diff --git a/src/app/utils/auth/auth.utils.go b/src/app/utils/auth/auth.utils.go index 18de087..5a05f51 100644 --- a/src/app/utils/auth/auth.utils.go +++ b/src/app/utils/auth/auth.utils.go @@ -2,8 +2,65 @@ package auth import ( "fmt" + "strconv" + "strings" + + "github.com/google/uuid" ) -func FormatPath(method string, path string) string { +func IsExisted(e map[string]struct{}, key string) bool { + _, ok := e[key] + if ok { + return true + } + return false +} + +func FormatPath(method string, path string, keys []string) string { + for _, key := range keys { + path = strings.Replace(path, key, ":id", 1) + } + return fmt.Sprintf("%v %v", method, path) } + +func FindIntFromStr(s string, sep string) []string { + spliteds := strings.Split(s, sep) + + var result []string + + for _, splited := range spliteds { + _, err := strconv.Atoi(splited) + if err == nil { + result = append(result, splited) + } + } + + return result +} + +func FindUUIDFromStr(s string, sep string) []string { + spliteds := strings.Split(s, sep) + + var result []string + + for _, splited := range spliteds { + _, err := uuid.Parse(splited) + if err == nil { + result = append(result, splited) + } + } + + return result +} + +func MergeStringSlice(s1 []string, s2 []string) []string { + return append(s1, s2...) +} + +func FindIDFromPath(path string) []string { + uuids := FindUUIDFromStr(path, "/") + ids := FindIntFromStr(path, "/") + + return MergeStringSlice(ids, uuids) +} diff --git a/src/app/utils/image/image.utils.go b/src/app/utils/image/image.utils.go new file mode 100644 index 0000000..705c34f --- /dev/null +++ b/src/app/utils/image/image.utils.go @@ -0,0 +1,54 @@ +package image + +import ( + "fmt" + + "github.com/isd-sgcu/johnjud-gateway/src/app/dto" + imageProto "github.com/isd-sgcu/johnjud-go-proto/johnjud/file/image/v1" +) + +func ProtoToDto(in *imageProto.Image) *dto.ImageResponse { + return &dto.ImageResponse{ + Id: in.Id, + Url: in.ImageUrl, + ObjectKey: in.ObjectKey, + } +} + +func ProtoToDtoList(in []*imageProto.Image) []*dto.ImageResponse { + var res []*dto.ImageResponse + for _, i := range in { + res = append(res, &dto.ImageResponse{ + Id: i.Id, + Url: i.ImageUrl, + ObjectKey: i.ObjectKey, + }) + } + return res +} + +func CreateDtoToProto(in *dto.UploadImageRequest) *imageProto.UploadImageRequest { + return &imageProto.UploadImageRequest{ + Filename: in.Filename, + Data: in.Data, + PetId: in.PetId, + } +} + +func MockImageList(n int) [][]*imageProto.Image { + var imagesList [][]*imageProto.Image + for i := 0; i <= n; i++ { + var images []*imageProto.Image + for j := 0; j <= 3; j++ { + images = append(images, &imageProto.Image{ + Id: fmt.Sprintf("%v%v", i, j), + PetId: fmt.Sprintf("%v%v", i, j), + ImageUrl: fmt.Sprintf("%v%v", i, j), + ObjectKey: fmt.Sprintf("%v%v", i, j), + }) + } + imagesList = append(imagesList, images) + } + + return imagesList +} diff --git a/src/app/utils/like/like.utils.go b/src/app/utils/like/like.utils.go new file mode 100644 index 0000000..1349010 --- /dev/null +++ b/src/app/utils/like/like.utils.go @@ -0,0 +1,33 @@ +package like + +import ( + "github.com/isd-sgcu/johnjud-gateway/src/app/dto" + likeProto "github.com/isd-sgcu/johnjud-go-proto/johnjud/backend/like/v1" +) + +func ProtoToDto(in *likeProto.Like) *dto.LikeResponse { + return &dto.LikeResponse{ + UserID: in.UserId, + PetID: in.PetId, + } +} + +func ProtoToDtoList(in []*likeProto.Like) []*dto.LikeResponse { + var res []*dto.LikeResponse + for _, i := range in { + res = append(res, &dto.LikeResponse{ + UserID: i.UserId, + PetID: i.PetId, + }) + } + return res +} + +func CreateDtoToProto(in *dto.CreateLikeRequest) *likeProto.CreateLikeRequest { + return &likeProto.CreateLikeRequest{ + Like: &likeProto.Like{ + UserId: in.UserID, + PetId: in.PetID, + }, + } +} diff --git a/src/app/utils/pet/pet.utils.go b/src/app/utils/pet/pet.utils.go index 0ed39e3..c768f29 100644 --- a/src/app/utils/pet/pet.utils.go +++ b/src/app/utils/pet/pet.utils.go @@ -1,7 +1,9 @@ package pet import ( + "errors" "fmt" + "strconv" "github.com/isd-sgcu/johnjud-gateway/src/app/dto" "github.com/isd-sgcu/johnjud-gateway/src/constant/pet" @@ -31,18 +33,18 @@ func ProtoToDto(in *petproto.Pet, images []*imgproto.Image) *dto.PetResponse { pet := &dto.PetResponse{ Id: in.Id, Type: in.Type, - Species: in.Species, Name: in.Name, Birthdate: in.Birthdate, Gender: pet.Gender(in.Gender), + Color: in.Color, + Pattern: in.Pattern, Habit: in.Habit, Caption: in.Caption, Status: pet.Status(in.Status), IsSterile: &in.IsSterile, IsVaccinated: &in.IsVaccinated, IsVisible: &in.IsVisible, - IsClubPet: &in.IsClubPet, - Background: in.Background, + Origin: in.Origin, Address: in.Address, Contact: in.Contact, AdoptBy: in.AdoptBy, @@ -55,19 +57,19 @@ func CreateDtoToProto(in *dto.CreatePetRequest) *petproto.CreatePetRequest { return &petproto.CreatePetRequest{ Pet: &petproto.Pet{ Type: in.Type, - Species: in.Species, Name: in.Name, Birthdate: in.Birthdate, - Gender: petproto.Gender(in.Gender), + Gender: string(in.Gender), + Color: in.Color, + Pattern: in.Pattern, Habit: in.Habit, Caption: in.Caption, Images: []*imgproto.Image{}, - Status: petproto.PetStatus(in.Status), + Status: string(in.Status), IsSterile: *in.IsSterile, IsVaccinated: *in.IsVaccinated, IsVisible: *in.IsVisible, - IsClubPet: *in.IsClubPet, - Background: in.Background, + Origin: in.Origin, Address: in.Address, Contact: in.Contact, AdoptBy: in.AdoptBy, @@ -78,68 +80,45 @@ func CreateDtoToProto(in *dto.CreatePetRequest) *petproto.CreatePetRequest { func UpdateDtoToProto(id string, in *dto.UpdatePetRequest) *petproto.UpdatePetRequest { req := &petproto.UpdatePetRequest{ Pet: &petproto.Pet{ - Id: id, - Type: in.Type, - Species: in.Species, - Name: in.Name, - Birthdate: in.Birthdate, - Gender: petproto.Gender(in.Gender), - Habit: in.Habit, - Caption: in.Caption, - Images: []*imgproto.Image{}, - Status: petproto.PetStatus(in.Status), - Background: in.Background, - Address: in.Address, - Contact: in.Contact, - AdoptBy: in.AdoptBy, + Id: id, + Type: in.Type, + Name: in.Name, + Birthdate: in.Birthdate, + Gender: string(in.Gender), + Color: in.Color, + Pattern: in.Pattern, + Habit: in.Habit, + Caption: in.Caption, + Images: []*imgproto.Image{}, + Status: string(in.Status), + Origin: in.Origin, + Address: in.Address, + Contact: in.Contact, + AdoptBy: in.AdoptBy, }, } - if in.IsClubPet == nil { - req.Pet.IsClubPet = false - } else { - req.Pet.IsClubPet = *in.IsClubPet - } - - if in.IsSterile == nil { - req.Pet.IsSterile = false - } else { - req.Pet.IsSterile = *in.IsSterile - } - - if in.IsVaccinated == nil { - req.Pet.IsVaccinated = false - } else { - req.Pet.IsVaccinated = *in.IsVaccinated - } - - if in.IsVisible == nil { - req.Pet.IsVisible = false - } else { - req.Pet.IsVisible = *in.IsVisible - } - return req } -func RawToDtoList(in []*petproto.Pet, imagesList [][]*imgproto.Image) []*dto.PetResponse { +func ProtoToDtoList(in []*petproto.Pet, imagesList [][]*imgproto.Image) []*dto.PetResponse { var resp []*dto.PetResponse for i, p := range in { pet := &dto.PetResponse{ Id: p.Id, Type: p.Type, - Species: p.Species, Name: p.Name, Birthdate: p.Birthdate, Gender: pet.Gender(p.Gender), + Color: p.Color, + Pattern: p.Pattern, Habit: p.Habit, Caption: p.Caption, Status: pet.Status(p.Status), IsSterile: &p.IsSterile, IsVaccinated: &p.IsVaccinated, IsVisible: &p.IsVisible, - IsClubPet: &p.IsClubPet, - Background: p.Background, + Origin: p.Origin, Address: p.Address, Contact: p.Contact, AdoptBy: p.AdoptBy, @@ -160,3 +139,73 @@ func extractImages(images []*imgproto.Image) []dto.ImageResponse { } return result } + +func FindAllDtoToProto(in *dto.FindAllPetRequest) *petproto.FindAllPetRequest { + return &petproto.FindAllPetRequest{ + Search: in.Search, + Type: in.Type, + Gender: in.Gender, + Color: in.Color, + Pattern: in.Pattern, + Age: in.Age, + Origin: in.Origin, + PageSize: int32(in.PageSize), + Page: int32(in.Page), + } +} + +func MetadataProtoToDto(in *petproto.FindAllPetMetaData) *dto.FindAllMetadata { + return &dto.FindAllMetadata{ + Page: int(in.Page), + TotalPages: int(in.TotalPages), + PageSize: int(in.PageSize), + Total: int(in.Total), + } +} + +func QueriesToFindAllDto(queries map[string]string) (*dto.FindAllPetRequest, error) { + request := &dto.FindAllPetRequest{ + Search: "", + Type: "", + Gender: "", + Color: "", + Pattern: "", + Age: "", + Origin: "", + PageSize: 0, + Page: 0, + } + + for q, v := range queries { + switch q { + case "search": + request.Search = v + case "type": + request.Type = v + case "gender": + request.Gender = v + case "color": + request.Color = v + case "pattern": + request.Pattern = v + case "age": + request.Age = v + case "origin": + request.Origin = v + case "pageSize": + pageSize, err := strconv.Atoi(v) + if err != nil { + return nil, errors.New("error pasring pageSize") + } + request.PageSize = pageSize + case "page": + page, err := strconv.Atoi(v) + if err != nil { + return nil, errors.New("error pasring page") + } + request.Page = page + } + } + + return request, nil +} diff --git a/src/constant/auth/auth.constant.go b/src/constant/auth/auth.constant.go index 2273fbe..281fc8c 100644 --- a/src/constant/auth/auth.constant.go +++ b/src/constant/auth/auth.constant.go @@ -4,8 +4,21 @@ var ExcludePath = map[string]struct{}{ "POST /auth/signup": {}, "POST /auth/signin": {}, "POST /auth/verify": {}, - "GET /pet/": {}, - "GET /adopt/": {}, + "GET /user/:id": {}, + "GET /pets": {}, + "GET /adopt": {}, +} + +var AdminPath = map[string]struct{}{ + "DELETE /user/:id": {}, + "POST /pets": {}, + "PUT /pets/:id": {}, + "PUT /pets/:id/visible": {}, + "DELETE /pets/:id": {}, + "POST /images/assign/:pet_id": {}, + "DELETE /images/:id": {}, + "POST /images/": {}, + "GET /images/:id": {}, } var VersionList = map[string]struct{}{ diff --git a/src/constant/like/like.constant.go b/src/constant/like/like.constant.go new file mode 100644 index 0000000..35960ae --- /dev/null +++ b/src/constant/like/like.constant.go @@ -0,0 +1,5 @@ +package like + +const FindLikeSuccessMessage = "find likes success" +const CreateLikeSuccessMessage = "create like success" +const DelteLikeSuccessMessage = "delete like success" diff --git a/src/constant/pet/pet.constant.go b/src/constant/pet/pet.constant.go index fe606fe..d2428f6 100644 --- a/src/constant/pet/pet.constant.go +++ b/src/constant/pet/pet.constant.go @@ -6,18 +6,18 @@ import ( "strings" ) -type Gender int +type Gender string const ( - MALE = 1 - FEMALE = 2 + MALE Gender = "male" + FEMALE Gender = "female" ) -type Status int +type Status string const ( - ADOPTED = 1 - FINDHOME = 2 + ADOPTED Status = "adopted" + FINDHOME Status = "findhome" ) func (g *Gender) UnmarshalJSON(data []byte) error { @@ -28,10 +28,10 @@ func (g *Gender) UnmarshalJSON(data []byte) error { s = strings.ToUpper(s) switch s { case "MALE": - *g = Gender(1) + *g = MALE return nil case "FEMALE": - *g = Gender(2) + *g = FEMALE return nil default: @@ -47,14 +47,14 @@ func (st *Status) UnmarshalJSON(data []byte) error { s = strings.ToUpper(s) switch s { case "ADOPTED": - *st = Status(1) + *st = ADOPTED return nil case "FINDHOME": - *st = Status(2) + *st = FINDHOME return nil default: - return errors.New("invalid gender") + return errors.New("invalid status") } } @@ -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" diff --git a/src/constant/user/user.constant.go b/src/constant/user/user.constant.go new file mode 100644 index 0000000..cf1991e --- /dev/null +++ b/src/constant/user/user.constant.go @@ -0,0 +1,12 @@ +package user + +const FindOneUserSuccessMessage = "find one user success" +const UpdateUserSuccessMessage = "update user success" +const DeleteUserSuccessMessage = "delete user success" + +type Role string + +const ( + USER Role = "user" + ADMIN Role = "admin" +) diff --git a/src/docs/docs.go b/src/docs/docs.go index de72062..ee19d5a 100644 --- a/src/docs/docs.go +++ b/src/docs/docs.go @@ -1,5 +1,4 @@ -// Code generated by swaggo/swag. DO NOT EDIT. - +// Package docs Code generated by swaggo/swag. DO NOT EDIT package docs import "github.com/swaggo/swag" @@ -234,9 +233,9 @@ const docTemplate = `{ } } }, - "/v1/pet/": { + "/v1/pets/": { "get": { - "description": "Return the data of pets if successfully", + "description": "Returns the data of pets if successful", "consumes": [ "application/json" ], @@ -244,17 +243,103 @@ const docTemplate = `{ "application/json" ], "tags": [ - "auth" + "pet" + ], + "summary": "finds all pets", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/dto.PetResponse" + } + } + }, + "500": { + "description": "Internal service error", + "schema": { + "$ref": "#/definitions/dto.ResponseInternalErr" + } + }, + "503": { + "description": "Service is down", + "schema": { + "$ref": "#/definitions/dto.ResponseServiceDownErr" + } + } + } + }, + "delete": { + "description": "Returns successful status if pet is successfully deleted", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "pet" + ], + "summary": "deletes pet", + "parameters": [ + { + "type": "string", + "description": "pet id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/dto.DeleteResponse" + } + }, + "400": { + "description": "Invalid request body", + "schema": { + "$ref": "#/definitions/dto.ResponseBadRequestErr" + } + }, + "500": { + "description": "Internal service error", + "schema": { + "$ref": "#/definitions/dto.ResponseInternalErr" + } + }, + "503": { + "description": "Service is down", + "schema": { + "$ref": "#/definitions/dto.ResponseServiceDownErr" + } + } + } + } + }, + "/v1/pets/create": { + "post": { + "description": "Returns the data of pet if successful", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "pet" ], - "summary": "find all pets", + "summary": "creates pet", "parameters": [ { "description": "pet dto", - "name": "signup", + "name": "create", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/dto.PetDto" + "$ref": "#/definitions/dto.CreatePetRequest" } } ], @@ -262,7 +347,7 @@ const docTemplate = `{ "201": { "description": "Created", "schema": { - "$ref": "#/definitions/dto.PetDto" + "$ref": "#/definitions/dto.PetResponse" } }, "400": { @@ -271,10 +356,229 @@ const docTemplate = `{ "$ref": "#/definitions/dto.ResponseBadRequestErr" } }, - "409": { - "description": "Duplicate email", + "500": { + "description": "Internal service error", "schema": { - "$ref": "#/definitions/dto.ResponseConflictErr" + "$ref": "#/definitions/dto.ResponseInternalErr" + } + }, + "503": { + "description": "Service is down", + "schema": { + "$ref": "#/definitions/dto.ResponseServiceDownErr" + } + } + } + } + }, + "/v1/pets/{id}": { + "get": { + "description": "Returns the data of a pet if successful", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "pet" + ], + "summary": "finds one pet", + "parameters": [ + { + "type": "string", + "description": "pet id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dto.PetResponse" + } + }, + "400": { + "description": "Invalid request body", + "schema": { + "$ref": "#/definitions/dto.ResponseBadRequestErr" + } + }, + "500": { + "description": "Internal service error", + "schema": { + "$ref": "#/definitions/dto.ResponseInternalErr" + } + }, + "503": { + "description": "Service is down", + "schema": { + "$ref": "#/definitions/dto.ResponseServiceDownErr" + } + } + } + }, + "put": { + "description": "Returns the data of pet if successfully", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "pet" + ], + "summary": "updates pet", + "parameters": [ + { + "description": "update pet dto", + "name": "update", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.UpdatePetRequest" + } + }, + { + "type": "string", + "description": "pet id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/dto.PetResponse" + } + }, + "400": { + "description": "Invalid request body", + "schema": { + "$ref": "#/definitions/dto.ResponseBadRequestErr" + } + }, + "500": { + "description": "Internal service error", + "schema": { + "$ref": "#/definitions/dto.ResponseInternalErr" + } + }, + "503": { + "description": "Service is down", + "schema": { + "$ref": "#/definitions/dto.ResponseServiceDownErr" + } + } + } + } + }, + "/v1/pets/{id}/adopt": { + "put": { + "description": "Return true if the pet is successfully adopted", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "pet" + ], + "summary": "Change a pet's adoptBy status", + "parameters": [ + { + "description": "adopt pet dto", + "name": "adoptDto", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.AdoptByRequest" + } + }, + { + "type": "string", + "description": "Pet ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/dto.AdoptByResponse" + } + }, + "400": { + "description": "Invalid request body", + "schema": { + "$ref": "#/definitions/dto.ResponseBadRequestErr" + } + }, + "500": { + "description": "Internal service error", + "schema": { + "$ref": "#/definitions/dto.ResponseInternalErr" + } + }, + "503": { + "description": "Service is down", + "schema": { + "$ref": "#/definitions/dto.ResponseServiceDownErr" + } + } + } + } + }, + "/v1/pets/{id}/visible": { + "put": { + "description": "Returns successful status if pet's IsVisible is successfully changed", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "pet" + ], + "summary": "changes pet's public visiblility", + "parameters": [ + { + "description": "changeView pet dto", + "name": "changeViewDto", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.ChangeViewPetRequest" + } + }, + { + "type": "string", + "description": "pet id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/dto.ChangeViewPetResponse" + } + }, + "400": { + "description": "Invalid request body", + "schema": { + "$ref": "#/definitions/dto.ResponseBadRequestErr" } }, "500": { @@ -294,6 +598,29 @@ const docTemplate = `{ } }, "definitions": { + "dto.AdoptByRequest": { + "type": "object", + "required": [ + "pet_id", + "user_id" + ], + "properties": { + "pet_id": { + "type": "string" + }, + "user_id": { + "type": "string" + } + } + }, + "dto.AdoptByResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + } + } + }, "dto.BadReqErrResponse": { "type": "object", "properties": { @@ -306,6 +633,102 @@ const docTemplate = `{ "value": {} } }, + "dto.ChangeViewPetRequest": { + "type": "object", + "required": [ + "visible" + ], + "properties": { + "visible": { + "type": "boolean" + } + } + }, + "dto.ChangeViewPetResponse": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "success": { + "type": "boolean" + } + } + }, + "dto.CreatePetRequest": { + "type": "object", + "required": [ + "birthdate", + "gender", + "habit", + "is_club_pet", + "is_sterile", + "is_vaccinated", + "is_visible", + "name", + "species", + "status", + "type" + ], + "properties": { + "address": { + "type": "string" + }, + "adopt_by": { + "type": "string" + }, + "background": { + "type": "string" + }, + "birthdate": { + "type": "string" + }, + "caption": { + "type": "string" + }, + "contact": { + "type": "string" + }, + "gender": { + "type": "integer", + "example": 1 + }, + "habit": { + "type": "string" + }, + "images": { + "type": "array", + "items": { + "type": "string" + } + }, + "is_club_pet": { + "type": "boolean" + }, + "is_sterile": { + "type": "boolean" + }, + "is_vaccinated": { + "type": "boolean" + }, + "is_visible": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "species": { + "type": "string" + }, + "status": { + "type": "integer", + "example": 1 + }, + "type": { + "type": "string" + } + } + }, "dto.Credential": { "type": "object", "properties": { @@ -323,6 +746,90 @@ const docTemplate = `{ } } }, + "dto.DeleteResponse": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "success": { + "type": "boolean" + } + } + }, + "dto.ImageResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "url": { + "type": "string" + } + } + }, + "dto.PetResponse": { + "type": "object", + "properties": { + "address": { + "type": "string" + }, + "adopt_by": { + "type": "string" + }, + "background": { + "type": "string" + }, + "birthdate": { + "type": "string" + }, + "caption": { + "type": "string" + }, + "contact": { + "type": "string" + }, + "gender": { + "type": "integer" + }, + "habit": { + "type": "string" + }, + "id": { + "type": "string" + }, + "images": { + "type": "array", + "items": { + "$ref": "#/definitions/dto.ImageResponse" + } + }, + "is_club_pet": { + "type": "boolean" + }, + "is_sterile": { + "type": "boolean" + }, + "is_vaccinated": { + "type": "boolean" + }, + "is_visible": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "species": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "type": { + "type": "string" + } + } + }, "dto.RefreshTokenRequest": { "type": "object", "required": [ @@ -334,9 +841,6 @@ const docTemplate = `{ } } }, - "dto.PetDto": { - "type": "object" - }, "dto.ResponseBadRequestErr": { "type": "object", "properties": { @@ -492,6 +996,65 @@ const docTemplate = `{ "type": "string" } } + }, + "dto.UpdatePetRequest": { + "type": "object", + "properties": { + "address": { + "type": "string" + }, + "adopt_by": { + "type": "string" + }, + "background": { + "type": "string" + }, + "birthdate": { + "type": "string" + }, + "caption": { + "type": "string" + }, + "contact": { + "type": "string" + }, + "gender": { + "type": "integer" + }, + "habit": { + "type": "string" + }, + "images": { + "type": "array", + "items": { + "type": "string" + } + }, + "is_club_pet": { + "type": "boolean" + }, + "is_sterile": { + "type": "boolean" + }, + "is_vaccinated": { + "type": "boolean" + }, + "is_visible": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "species": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "type": { + "type": "string" + } + } } }, "securityDefinitions": { diff --git a/src/docs/swagger.json b/src/docs/swagger.json index 1303f34..deaf951 100644 --- a/src/docs/swagger.json +++ b/src/docs/swagger.json @@ -229,9 +229,9 @@ } } }, - "/v1/pet/": { + "/v1/pets/": { "get": { - "description": "Return the data of pets if successfully", + "description": "Returns the data of pets if successful", "consumes": [ "application/json" ], @@ -239,17 +239,103 @@ "application/json" ], "tags": [ - "auth" + "pet" + ], + "summary": "finds all pets", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/dto.PetResponse" + } + } + }, + "500": { + "description": "Internal service error", + "schema": { + "$ref": "#/definitions/dto.ResponseInternalErr" + } + }, + "503": { + "description": "Service is down", + "schema": { + "$ref": "#/definitions/dto.ResponseServiceDownErr" + } + } + } + }, + "delete": { + "description": "Returns successful status if pet is successfully deleted", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "pet" + ], + "summary": "deletes pet", + "parameters": [ + { + "type": "string", + "description": "pet id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/dto.DeleteResponse" + } + }, + "400": { + "description": "Invalid request body", + "schema": { + "$ref": "#/definitions/dto.ResponseBadRequestErr" + } + }, + "500": { + "description": "Internal service error", + "schema": { + "$ref": "#/definitions/dto.ResponseInternalErr" + } + }, + "503": { + "description": "Service is down", + "schema": { + "$ref": "#/definitions/dto.ResponseServiceDownErr" + } + } + } + } + }, + "/v1/pets/create": { + "post": { + "description": "Returns the data of pet if successful", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "pet" ], - "summary": "find all pets", + "summary": "creates pet", "parameters": [ { "description": "pet dto", - "name": "signup", + "name": "create", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/dto.PetDto" + "$ref": "#/definitions/dto.CreatePetRequest" } } ], @@ -257,7 +343,7 @@ "201": { "description": "Created", "schema": { - "$ref": "#/definitions/dto.PetDto" + "$ref": "#/definitions/dto.PetResponse" } }, "400": { @@ -266,10 +352,229 @@ "$ref": "#/definitions/dto.ResponseBadRequestErr" } }, - "409": { - "description": "Duplicate email", + "500": { + "description": "Internal service error", "schema": { - "$ref": "#/definitions/dto.ResponseConflictErr" + "$ref": "#/definitions/dto.ResponseInternalErr" + } + }, + "503": { + "description": "Service is down", + "schema": { + "$ref": "#/definitions/dto.ResponseServiceDownErr" + } + } + } + } + }, + "/v1/pets/{id}": { + "get": { + "description": "Returns the data of a pet if successful", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "pet" + ], + "summary": "finds one pet", + "parameters": [ + { + "type": "string", + "description": "pet id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dto.PetResponse" + } + }, + "400": { + "description": "Invalid request body", + "schema": { + "$ref": "#/definitions/dto.ResponseBadRequestErr" + } + }, + "500": { + "description": "Internal service error", + "schema": { + "$ref": "#/definitions/dto.ResponseInternalErr" + } + }, + "503": { + "description": "Service is down", + "schema": { + "$ref": "#/definitions/dto.ResponseServiceDownErr" + } + } + } + }, + "put": { + "description": "Returns the data of pet if successfully", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "pet" + ], + "summary": "updates pet", + "parameters": [ + { + "description": "update pet dto", + "name": "update", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.UpdatePetRequest" + } + }, + { + "type": "string", + "description": "pet id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/dto.PetResponse" + } + }, + "400": { + "description": "Invalid request body", + "schema": { + "$ref": "#/definitions/dto.ResponseBadRequestErr" + } + }, + "500": { + "description": "Internal service error", + "schema": { + "$ref": "#/definitions/dto.ResponseInternalErr" + } + }, + "503": { + "description": "Service is down", + "schema": { + "$ref": "#/definitions/dto.ResponseServiceDownErr" + } + } + } + } + }, + "/v1/pets/{id}/adopt": { + "put": { + "description": "Return true if the pet is successfully adopted", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "pet" + ], + "summary": "Change a pet's adoptBy status", + "parameters": [ + { + "description": "adopt pet dto", + "name": "adoptDto", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.AdoptByRequest" + } + }, + { + "type": "string", + "description": "Pet ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/dto.AdoptByResponse" + } + }, + "400": { + "description": "Invalid request body", + "schema": { + "$ref": "#/definitions/dto.ResponseBadRequestErr" + } + }, + "500": { + "description": "Internal service error", + "schema": { + "$ref": "#/definitions/dto.ResponseInternalErr" + } + }, + "503": { + "description": "Service is down", + "schema": { + "$ref": "#/definitions/dto.ResponseServiceDownErr" + } + } + } + } + }, + "/v1/pets/{id}/visible": { + "put": { + "description": "Returns successful status if pet's IsVisible is successfully changed", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "pet" + ], + "summary": "changes pet's public visiblility", + "parameters": [ + { + "description": "changeView pet dto", + "name": "changeViewDto", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.ChangeViewPetRequest" + } + }, + { + "type": "string", + "description": "pet id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/dto.ChangeViewPetResponse" + } + }, + "400": { + "description": "Invalid request body", + "schema": { + "$ref": "#/definitions/dto.ResponseBadRequestErr" } }, "500": { @@ -289,6 +594,29 @@ } }, "definitions": { + "dto.AdoptByRequest": { + "type": "object", + "required": [ + "pet_id", + "user_id" + ], + "properties": { + "pet_id": { + "type": "string" + }, + "user_id": { + "type": "string" + } + } + }, + "dto.AdoptByResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + } + } + }, "dto.BadReqErrResponse": { "type": "object", "properties": { @@ -301,6 +629,102 @@ "value": {} } }, + "dto.ChangeViewPetRequest": { + "type": "object", + "required": [ + "visible" + ], + "properties": { + "visible": { + "type": "boolean" + } + } + }, + "dto.ChangeViewPetResponse": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "success": { + "type": "boolean" + } + } + }, + "dto.CreatePetRequest": { + "type": "object", + "required": [ + "birthdate", + "gender", + "habit", + "is_club_pet", + "is_sterile", + "is_vaccinated", + "is_visible", + "name", + "species", + "status", + "type" + ], + "properties": { + "address": { + "type": "string" + }, + "adopt_by": { + "type": "string" + }, + "background": { + "type": "string" + }, + "birthdate": { + "type": "string" + }, + "caption": { + "type": "string" + }, + "contact": { + "type": "string" + }, + "gender": { + "type": "integer", + "example": 1 + }, + "habit": { + "type": "string" + }, + "images": { + "type": "array", + "items": { + "type": "string" + } + }, + "is_club_pet": { + "type": "boolean" + }, + "is_sterile": { + "type": "boolean" + }, + "is_vaccinated": { + "type": "boolean" + }, + "is_visible": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "species": { + "type": "string" + }, + "status": { + "type": "integer", + "example": 1 + }, + "type": { + "type": "string" + } + } + }, "dto.Credential": { "type": "object", "properties": { @@ -318,6 +742,90 @@ } } }, + "dto.DeleteResponse": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "success": { + "type": "boolean" + } + } + }, + "dto.ImageResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "url": { + "type": "string" + } + } + }, + "dto.PetResponse": { + "type": "object", + "properties": { + "address": { + "type": "string" + }, + "adopt_by": { + "type": "string" + }, + "background": { + "type": "string" + }, + "birthdate": { + "type": "string" + }, + "caption": { + "type": "string" + }, + "contact": { + "type": "string" + }, + "gender": { + "type": "integer" + }, + "habit": { + "type": "string" + }, + "id": { + "type": "string" + }, + "images": { + "type": "array", + "items": { + "$ref": "#/definitions/dto.ImageResponse" + } + }, + "is_club_pet": { + "type": "boolean" + }, + "is_sterile": { + "type": "boolean" + }, + "is_vaccinated": { + "type": "boolean" + }, + "is_visible": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "species": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "type": { + "type": "string" + } + } + }, "dto.RefreshTokenRequest": { "type": "object", "required": [ @@ -329,9 +837,6 @@ } } }, - "dto.PetDto": { - "type": "object" - }, "dto.ResponseBadRequestErr": { "type": "object", "properties": { @@ -487,6 +992,65 @@ "type": "string" } } + }, + "dto.UpdatePetRequest": { + "type": "object", + "properties": { + "address": { + "type": "string" + }, + "adopt_by": { + "type": "string" + }, + "background": { + "type": "string" + }, + "birthdate": { + "type": "string" + }, + "caption": { + "type": "string" + }, + "contact": { + "type": "string" + }, + "gender": { + "type": "integer" + }, + "habit": { + "type": "string" + }, + "images": { + "type": "array", + "items": { + "type": "string" + } + }, + "is_club_pet": { + "type": "boolean" + }, + "is_sterile": { + "type": "boolean" + }, + "is_vaccinated": { + "type": "boolean" + }, + "is_visible": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "species": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "type": { + "type": "string" + } + } } }, "securityDefinitions": { diff --git a/src/docs/swagger.yaml b/src/docs/swagger.yaml index b6ed6da..e585f99 100644 --- a/src/docs/swagger.yaml +++ b/src/docs/swagger.yaml @@ -1,4 +1,19 @@ definitions: + dto.AdoptByRequest: + properties: + pet_id: + type: string + user_id: + type: string + required: + - pet_id + - user_id + type: object + dto.AdoptByResponse: + properties: + success: + type: boolean + type: object dto.BadReqErrResponse: properties: failed_field: @@ -7,6 +22,73 @@ definitions: type: string value: {} type: object + dto.ChangeViewPetRequest: + properties: + visible: + type: boolean + required: + - visible + type: object + dto.ChangeViewPetResponse: + properties: + success: + type: boolean + required: + - success + type: object + dto.CreatePetRequest: + properties: + address: + type: string + adopt_by: + type: string + background: + type: string + birthdate: + type: string + caption: + type: string + contact: + type: string + gender: + example: 1 + type: integer + habit: + type: string + images: + items: + type: string + type: array + is_club_pet: + type: boolean + is_sterile: + type: boolean + is_vaccinated: + type: boolean + is_visible: + type: boolean + name: + type: string + species: + type: string + status: + example: 1 + type: integer + type: + type: string + required: + - birthdate + - gender + - habit + - is_club_pet + - is_sterile + - is_vaccinated + - is_visible + - name + - species + - status + - type + type: object dto.Credential: properties: access_token: @@ -19,6 +101,61 @@ definitions: example: e7e84d54-7518-4... type: string type: object + dto.DeleteResponse: + properties: + success: + type: boolean + required: + - success + type: object + dto.ImageResponse: + properties: + id: + type: string + url: + type: string + type: object + dto.PetResponse: + properties: + address: + type: string + adopt_by: + type: string + background: + type: string + birthdate: + type: string + caption: + type: string + contact: + type: string + gender: + type: integer + habit: + type: string + id: + type: string + images: + items: + $ref: '#/definitions/dto.ImageResponse' + type: array + is_club_pet: + type: boolean + is_sterile: + type: boolean + is_vaccinated: + type: boolean + is_visible: + type: boolean + name: + type: string + species: + type: string + status: + type: integer + type: + type: string + type: object dto.RefreshTokenRequest: properties: refresh_token: @@ -26,8 +163,6 @@ definitions: required: - refresh_token type: object - dto.PetDto: - type: object dto.ResponseBadRequestErr: properties: data: @@ -137,6 +272,45 @@ definitions: lastname: type: string type: object + dto.UpdatePetRequest: + properties: + address: + type: string + adopt_by: + type: string + background: + type: string + birthdate: + type: string + caption: + type: string + contact: + type: string + gender: + type: integer + habit: + type: string + images: + items: + type: string + type: array + is_club_pet: + type: boolean + is_sterile: + type: boolean + is_vaccinated: + type: boolean + is_visible: + type: boolean + name: + type: string + species: + type: string + status: + type: integer + type: + type: string + type: object info: contact: email: sd.team.sgcu@gmail.com @@ -287,33 +461,162 @@ paths: summary: Signup user tags: - auth - /v1/pet/: + /v1/pets/: + delete: + consumes: + - application/json + description: Returns successful status if pet is successfully deleted + parameters: + - description: pet id + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/dto.DeleteResponse' + "400": + description: Invalid request body + schema: + $ref: '#/definitions/dto.ResponseBadRequestErr' + "500": + description: Internal service error + schema: + $ref: '#/definitions/dto.ResponseInternalErr' + "503": + description: Service is down + schema: + $ref: '#/definitions/dto.ResponseServiceDownErr' + summary: deletes pet + tags: + - pet get: consumes: - application/json - description: Return the data of pets if successfully + description: Returns the data of pets if successful + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/dto.PetResponse' + type: array + "500": + description: Internal service error + schema: + $ref: '#/definitions/dto.ResponseInternalErr' + "503": + description: Service is down + schema: + $ref: '#/definitions/dto.ResponseServiceDownErr' + summary: finds all pets + tags: + - pet + /v1/pets/{id}: + get: + consumes: + - application/json + description: Returns the data of a pet if successful parameters: - - description: pet dto + - description: pet id + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/dto.PetResponse' + "400": + description: Invalid request body + schema: + $ref: '#/definitions/dto.ResponseBadRequestErr' + "500": + description: Internal service error + schema: + $ref: '#/definitions/dto.ResponseInternalErr' + "503": + description: Service is down + schema: + $ref: '#/definitions/dto.ResponseServiceDownErr' + summary: finds one pet + tags: + - pet + put: + consumes: + - application/json + description: Returns the data of pet if successfully + parameters: + - description: update pet dto in: body - name: signup + name: update required: true schema: - $ref: '#/definitions/dto.PetDto' + $ref: '#/definitions/dto.UpdatePetRequest' + - description: pet id + in: path + name: id + required: true + type: string produces: - application/json responses: "201": description: Created schema: - $ref: '#/definitions/dto.PetDto' + $ref: '#/definitions/dto.PetResponse' "400": description: Invalid request body schema: $ref: '#/definitions/dto.ResponseBadRequestErr' - "409": - description: Duplicate email + "500": + description: Internal service error schema: - $ref: '#/definitions/dto.ResponseConflictErr' + $ref: '#/definitions/dto.ResponseInternalErr' + "503": + description: Service is down + schema: + $ref: '#/definitions/dto.ResponseServiceDownErr' + summary: updates pet + tags: + - pet + /v1/pets/{id}/adopt: + put: + consumes: + - application/json + description: Return true if the pet is successfully adopted + parameters: + - description: adopt pet dto + in: body + name: adoptDto + required: true + schema: + $ref: '#/definitions/dto.AdoptByRequest' + - description: Pet ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/dto.AdoptByResponse' + "400": + description: Invalid request body + schema: + $ref: '#/definitions/dto.ResponseBadRequestErr' "500": description: Internal service error schema: @@ -322,9 +625,82 @@ paths: description: Service is down schema: $ref: '#/definitions/dto.ResponseServiceDownErr' - summary: find all pets + summary: Change a pet's adoptBy status tags: - - auth + - pet + /v1/pets/{id}/visible: + put: + consumes: + - application/json + description: Returns successful status if pet's IsVisible is successfully changed + parameters: + - description: changeView pet dto + in: body + name: changeViewDto + required: true + schema: + $ref: '#/definitions/dto.ChangeViewPetRequest' + - description: pet id + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/dto.ChangeViewPetResponse' + "400": + description: Invalid request body + schema: + $ref: '#/definitions/dto.ResponseBadRequestErr' + "500": + description: Internal service error + schema: + $ref: '#/definitions/dto.ResponseInternalErr' + "503": + description: Service is down + schema: + $ref: '#/definitions/dto.ResponseServiceDownErr' + summary: changes pet's public visiblility + tags: + - pet + /v1/pets/create: + post: + consumes: + - application/json + description: Returns the data of pet if successful + parameters: + - description: pet dto + in: body + name: create + required: true + schema: + $ref: '#/definitions/dto.CreatePetRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/dto.PetResponse' + "400": + description: Invalid request body + schema: + $ref: '#/definitions/dto.ResponseBadRequestErr' + "500": + description: Internal service error + schema: + $ref: '#/definitions/dto.ResponseInternalErr' + "503": + description: Service is down + schema: + $ref: '#/definitions/dto.ResponseServiceDownErr' + summary: creates pet + tags: + - pet schemes: - https - http diff --git a/src/main.go b/src/main.go index aae156e..8e785da 100644 --- a/src/main.go +++ b/src/main.go @@ -12,12 +12,15 @@ import ( authHdr "github.com/isd-sgcu/johnjud-gateway/src/app/handler/auth" healthcheck "github.com/isd-sgcu/johnjud-gateway/src/app/handler/healthcheck" + imageHdr "github.com/isd-sgcu/johnjud-gateway/src/app/handler/image" + likeHdr "github.com/isd-sgcu/johnjud-gateway/src/app/handler/like" petHdr "github.com/isd-sgcu/johnjud-gateway/src/app/handler/pet" userHdr "github.com/isd-sgcu/johnjud-gateway/src/app/handler/user" guard "github.com/isd-sgcu/johnjud-gateway/src/app/middleware/auth" "github.com/isd-sgcu/johnjud-gateway/src/app/router" authSvc "github.com/isd-sgcu/johnjud-gateway/src/app/service/auth" imageSvc "github.com/isd-sgcu/johnjud-gateway/src/app/service/image" + likeSvc "github.com/isd-sgcu/johnjud-gateway/src/app/service/like" petSvc "github.com/isd-sgcu/johnjud-gateway/src/app/service/pet" userSvc "github.com/isd-sgcu/johnjud-gateway/src/app/service/user" "github.com/isd-sgcu/johnjud-gateway/src/app/validator" @@ -25,6 +28,7 @@ import ( "github.com/isd-sgcu/johnjud-gateway/src/constant/auth" authProto "github.com/isd-sgcu/johnjud-go-proto/johnjud/auth/auth/v1" userProto "github.com/isd-sgcu/johnjud-go-proto/johnjud/auth/user/v1" + likeProto "github.com/isd-sgcu/johnjud-go-proto/johnjud/backend/like/v1" petProto "github.com/isd-sgcu/johnjud-go-proto/johnjud/backend/pet/v1" imageProto "github.com/isd-sgcu/johnjud-go-proto/johnjud/file/image/v1" "github.com/rs/zerolog/log" @@ -100,19 +104,25 @@ func main() { authService := authSvc.NewService(authClient) authHandler := authHdr.NewHandler(authService, userService, v) - authGuard := guard.NewAuthGuard(authService, auth.ExcludePath, conf.App, auth.VersionList) + authGuard := guard.NewAuthGuard(authService, auth.ExcludePath, auth.AdminPath, conf.App, auth.VersionList) imageClient := imageProto.NewImageServiceClient(fileConn) imageService := imageSvc.NewService(imageClient) + imageHandler := imageHdr.NewHandler(imageService, v) petClient := petProto.NewPetServiceClient(backendConn) petService := petSvc.NewService(petClient) petHandler := petHdr.NewHandler(petService, imageService, v) + likeClient := likeProto.NewLikeServiceClient(backendConn) + likeService := likeSvc.NewService(likeClient) + likeHandler := likeHdr.NewHandler(likeService, v) + r := router.NewFiberRouter(&authGuard, conf.App) r.GetUser("/:id", userHandler.FindOne) - r.PutUser("/", userHandler.Update) + r.PutUser("", userHandler.Update) + r.DeleteUser("/:id", userHandler.Delete) r.PostAuth("/signup", authHandler.Signup) r.PostAuth("/signin", authHandler.SignIn) @@ -120,15 +130,24 @@ func main() { //r.PostAuth("/me", authHandler.Validate) r.PostAuth("/refreshToken", authHandler.RefreshToken) - r.GetHealthCheck("/", hc.HealthCheck) + r.GetHealthCheck("", hc.HealthCheck) - r.GetPet("/", petHandler.FindAll) + r.GetPet("", petHandler.FindAll) r.GetPet("/:id", petHandler.FindOne) - r.PostPet("/create", petHandler.Create) + r.PostPet("", petHandler.Create) r.PutPet("/:id", petHandler.Update) + r.PutPet("/:id/adopt", petHandler.Adopt) r.PutPet("/:id/visible", petHandler.ChangeView) r.DeletePet("/:id", petHandler.Delete) + r.GetLike("/:id", likeHandler.FindByUserId) + r.PostLike("", likeHandler.Create) + r.DeleteLike("/:id", likeHandler.Delete) + + r.PostImage("/", imageHandler.Upload) + r.DeleteImage("/:id", imageHandler.Delete) + r.PostImage("/assign", imageHandler.AssignPet) + v1 := router.NewAPIv1(r, conf.App) go func() { diff --git a/src/mocks/client/auth/auth.mock.go b/src/mocks/client/auth/auth.mock.go index 873ce2e..7474f16 100644 --- a/src/mocks/client/auth/auth.mock.go +++ b/src/mocks/client/auth/auth.mock.go @@ -2,6 +2,7 @@ package auth import ( "context" + authProto "github.com/isd-sgcu/johnjud-go-proto/johnjud/auth/auth/v1" "github.com/stretchr/testify/mock" "google.golang.org/grpc" @@ -45,3 +46,17 @@ func (m *AuthClientMock) SignOut(_ context.Context, in *authProto.SignOutRequest err, _ := args.Get(1).(error) return resp, err } + +func (m *AuthClientMock) ForgotPassword(_ context.Context, in *authProto.ForgotPasswordRequest, _ ...grpc.CallOption) (*authProto.ForgotPasswordResponse, error) { + args := m.Called(in) + resp, _ := args.Get(0).(*authProto.ForgotPasswordResponse) + err, _ := args.Get(1).(error) + return resp, err +} + +func (m *AuthClientMock) ResetPassword(_ context.Context, in *authProto.ResetPasswordRequest, _ ...grpc.CallOption) (*authProto.ResetPasswordResponse, error) { + args := m.Called(in) + resp, _ := args.Get(0).(*authProto.ResetPasswordResponse) + err, _ := args.Get(1).(error) + return resp, err +} diff --git a/src/mocks/client/like/like.mock.go b/src/mocks/client/like/like.mock.go new file mode 100644 index 0000000..0bb7bd8 --- /dev/null +++ b/src/mocks/client/like/like.mock.go @@ -0,0 +1,38 @@ +package like + +import ( + "context" + + likeProto "github.com/isd-sgcu/johnjud-go-proto/johnjud/backend/like/v1" + "github.com/stretchr/testify/mock" + "google.golang.org/grpc" +) + +type LikeClientMock struct { + mock.Mock +} + +func (c *LikeClientMock) FindByUserId(_ context.Context, in *likeProto.FindLikeByUserIdRequest, _ ...grpc.CallOption) (res *likeProto.FindLikeByUserIdResponse, err error) { + args := c.Called(in) + + if args.Get(0) != nil { + res = args.Get(0).(*likeProto.FindLikeByUserIdResponse) + } + return res, args.Error(1) +} +func (c *LikeClientMock) Create(_ context.Context, in *likeProto.CreateLikeRequest, _ ...grpc.CallOption) (res *likeProto.CreateLikeResponse, err error) { + args := c.Called(in) + + if args.Get(0) != nil { + res = args.Get(0).(*likeProto.CreateLikeResponse) + } + return res, args.Error(1) +} +func (c *LikeClientMock) Delete(_ context.Context, in *likeProto.DeleteLikeRequest, _ ...grpc.CallOption) (res *likeProto.DeleteLikeResponse, err error) { + args := c.Called(in) + + if args.Get(0) != nil { + res = args.Get(0).(*likeProto.DeleteLikeResponse) + } + return res, args.Error(1) +} diff --git a/src/mocks/client/pet/pet.mock.go b/src/mocks/client/pet/pet.mock.go index 5148750..10554c2 100644 --- a/src/mocks/client/pet/pet.mock.go +++ b/src/mocks/client/pet/pet.mock.go @@ -12,7 +12,7 @@ type PetClientMock struct { mock.Mock } -func (c *PetClientMock) AdoptPet(ctx context.Context, in *petProto.AdoptPetRequest, opts ...grpc.CallOption) (res *petProto.AdoptPetResponse, err error) { +func (c *PetClientMock) AdoptPet(_ context.Context, in *petProto.AdoptPetRequest, _ ...grpc.CallOption) (res *petProto.AdoptPetResponse, err error) { args := c.Called(in) if args.Get(0) != nil { @@ -21,7 +21,7 @@ func (c *PetClientMock) AdoptPet(ctx context.Context, in *petProto.AdoptPetReque return res, args.Error(1) } -func (c *PetClientMock) FindAll(ctx context.Context, in *petProto.FindAllPetRequest, opts ...grpc.CallOption) (res *petProto.FindAllPetResponse, err error) { +func (c *PetClientMock) FindAll(_ context.Context, in *petProto.FindAllPetRequest, _ ...grpc.CallOption) (res *petProto.FindAllPetResponse, err error) { args := c.Called(in) if args.Get(0) != nil { @@ -30,7 +30,7 @@ func (c *PetClientMock) FindAll(ctx context.Context, in *petProto.FindAllPetRequ return res, args.Error(1) } -func (c *PetClientMock) FindOne(ctx context.Context, in *petProto.FindOnePetRequest, opts ...grpc.CallOption) (res *petProto.FindOnePetResponse, err error) { +func (c *PetClientMock) FindOne(_ context.Context, in *petProto.FindOnePetRequest, _ ...grpc.CallOption) (res *petProto.FindOnePetResponse, err error) { args := c.Called(in) if args.Get(0) != nil { @@ -39,7 +39,7 @@ func (c *PetClientMock) FindOne(ctx context.Context, in *petProto.FindOnePetRequ return res, args.Error(1) } -func (c *PetClientMock) Create(ctx context.Context, in *petProto.CreatePetRequest, opts ...grpc.CallOption) (res *petProto.CreatePetResponse, err error) { +func (c *PetClientMock) Create(_ context.Context, in *petProto.CreatePetRequest, _ ...grpc.CallOption) (res *petProto.CreatePetResponse, err error) { args := c.Called(in) if args.Get(0) != nil { @@ -49,7 +49,7 @@ func (c *PetClientMock) Create(ctx context.Context, in *petProto.CreatePetReques return res, args.Error(1) } -func (c *PetClientMock) Update(ctx context.Context, in *petProto.UpdatePetRequest, opts ...grpc.CallOption) (res *petProto.UpdatePetResponse, err error) { +func (c *PetClientMock) Update(_ context.Context, in *petProto.UpdatePetRequest, _ ...grpc.CallOption) (res *petProto.UpdatePetResponse, err error) { args := c.Called(in) if args.Get(0) != nil { @@ -59,7 +59,7 @@ func (c *PetClientMock) Update(ctx context.Context, in *petProto.UpdatePetReques return res, args.Error(1) } -func (c *PetClientMock) ChangeView(ctx context.Context, in *petProto.ChangeViewPetRequest, opts ...grpc.CallOption) (res *petProto.ChangeViewPetResponse, err error) { +func (c *PetClientMock) ChangeView(_ context.Context, in *petProto.ChangeViewPetRequest, _ ...grpc.CallOption) (res *petProto.ChangeViewPetResponse, err error) { args := c.Called(in) if args.Get(0) != nil { @@ -69,7 +69,7 @@ func (c *PetClientMock) ChangeView(ctx context.Context, in *petProto.ChangeViewP return res, args.Error(1) } -func (c *PetClientMock) Delete(ctx context.Context, in *petProto.DeletePetRequest, opts ...grpc.CallOption) (res *petProto.DeletePetResponse, err error) { +func (c *PetClientMock) Delete(_ context.Context, in *petProto.DeletePetRequest, _ ...grpc.CallOption) (res *petProto.DeletePetResponse, err error) { args := c.Called(in) if args.Get(0) != nil { diff --git a/src/mocks/client/user/user.mock.go b/src/mocks/client/user/user.mock.go new file mode 100644 index 0000000..7e54f5f --- /dev/null +++ b/src/mocks/client/user/user.mock.go @@ -0,0 +1,41 @@ +package user + +import ( + "context" + + proto "github.com/isd-sgcu/johnjud-go-proto/johnjud/auth/user/v1" + "github.com/stretchr/testify/mock" + "google.golang.org/grpc" +) + +type UserClientMock struct { + mock.Mock +} + +func (c *UserClientMock) FindOne(_ context.Context, in *proto.FindOneUserRequest, _ ...grpc.CallOption) (res *proto.FindOneUserResponse, err error) { + args := c.Called(in) + + if args.Get(0) != nil { + res = args.Get(0).(*proto.FindOneUserResponse) + } + return res, args.Error(1) +} +func (c *UserClientMock) Update(_ context.Context, in *proto.UpdateUserRequest, _ ...grpc.CallOption) (res *proto.UpdateUserResponse, err error) { + args := c.Called(in) + + if args.Get(0) != nil { + res = args.Get(0).(*proto.UpdateUserResponse) + } + + return res, args.Error(1) +} + +func (c *UserClientMock) Delete(_ context.Context, in *proto.DeleteUserRequest, _ ...grpc.CallOption) (res *proto.DeleteUserResponse, err error) { + args := c.Called(in) + + if args.Get(0) != nil { + res = args.Get(0).(*proto.DeleteUserResponse) + } + + return res, args.Error(1) +} diff --git a/src/mocks/router/context.mock.go b/src/mocks/router/context.mock.go index f2238b6..9db2501 100644 --- a/src/mocks/router/context.mock.go +++ b/src/mocks/router/context.mock.go @@ -1,5 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: ./src/app/router/context.go +// +// Generated by this command: +// +// mockgen -source ./src/app/router/context.go -destination ./src/mocks/router/context.mock.go +// // Package mock_router is a generated GoMock package. package mock_router @@ -34,7 +39,7 @@ func (m *MockIContext) EXPECT() *MockIContextMockRecorder { } // Bind mocks base method. -func (m *MockIContext) Bind(arg0 interface{}) error { +func (m *MockIContext) Bind(arg0 any) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Bind", arg0) ret0, _ := ret[0].(error) @@ -42,7 +47,7 @@ func (m *MockIContext) Bind(arg0 interface{}) error { } // Bind indicates an expected call of Bind. -func (mr *MockIContextMockRecorder) Bind(arg0 interface{}) *gomock.Call { +func (mr *MockIContextMockRecorder) Bind(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Bind", reflect.TypeOf((*MockIContext)(nil).Bind), arg0) } @@ -63,13 +68,13 @@ func (mr *MockIContextMockRecorder) ID() *gomock.Call { } // JSON mocks base method. -func (m *MockIContext) JSON(arg0 int, arg1 interface{}) { +func (m *MockIContext) JSON(arg0 int, arg1 any) { m.ctrl.T.Helper() m.ctrl.Call(m, "JSON", arg0, arg1) } // JSON indicates an expected call of JSON. -func (mr *MockIContextMockRecorder) JSON(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockIContextMockRecorder) JSON(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "JSON", reflect.TypeOf((*MockIContext)(nil).JSON), arg0, arg1) } @@ -112,7 +117,7 @@ func (m *MockIContext) Param(arg0 string) (string, error) { } // Param indicates an expected call of Param. -func (mr *MockIContextMockRecorder) Param(arg0 interface{}) *gomock.Call { +func (mr *MockIContextMockRecorder) Param(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Param", reflect.TypeOf((*MockIContext)(nil).Param), arg0) } @@ -131,6 +136,34 @@ func (mr *MockIContextMockRecorder) Path() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Path", reflect.TypeOf((*MockIContext)(nil).Path)) } +// Queries mocks base method. +func (m *MockIContext) Queries() map[string]string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Queries") + ret0, _ := ret[0].(map[string]string) + return ret0 +} + +// Queries indicates an expected call of Queries. +func (mr *MockIContextMockRecorder) Queries() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Queries", reflect.TypeOf((*MockIContext)(nil).Queries)) +} + +// Role mocks base method. +func (m *MockIContext) Role() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Role") + ret0, _ := ret[0].(string) + return ret0 +} + +// Role indicates an expected call of Role. +func (mr *MockIContextMockRecorder) Role() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Role", reflect.TypeOf((*MockIContext)(nil).Role)) +} + // StoreValue mocks base method. func (m *MockIContext) StoreValue(arg0, arg1 string) { m.ctrl.T.Helper() @@ -138,7 +171,7 @@ func (m *MockIContext) StoreValue(arg0, arg1 string) { } // StoreValue indicates an expected call of StoreValue. -func (mr *MockIContextMockRecorder) StoreValue(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockIContextMockRecorder) StoreValue(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StoreValue", reflect.TypeOf((*MockIContext)(nil).StoreValue), arg0, arg1) } diff --git a/src/mocks/service/image/image.mock.go b/src/mocks/service/image/image.mock.go index 09a1ae0..6e7fb6b 100644 --- a/src/mocks/service/image/image.mock.go +++ b/src/mocks/service/image/image.mock.go @@ -1,5 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: ./src/pkg/service/image/image.service.go +// +// Generated by this command: +// +// mockgen -source ./src/pkg/service/image/image.service.go -destination ./src/mocks/service/image/image.mock.go +// // Package mock_image is a generated GoMock package. package mock_image @@ -7,9 +12,8 @@ package mock_image import ( reflect "reflect" - gomock "github.com/golang/mock/gomock" dto "github.com/isd-sgcu/johnjud-gateway/src/app/dto" - v1 "github.com/isd-sgcu/johnjud-go-proto/johnjud/file/image/v1" + gomock "github.com/golang/mock/gomock" ) // MockService is a mock of Service interface. @@ -35,47 +39,62 @@ func (m *MockService) EXPECT() *MockServiceMockRecorder { return m.recorder } +// AssignPet mocks base method. +func (m *MockService) AssignPet(arg0 *dto.AssignPetRequest) (*dto.AssignPetResponse, *dto.ResponseErr) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AssignPet", arg0) + ret0, _ := ret[0].(*dto.AssignPetResponse) + ret1, _ := ret[1].(*dto.ResponseErr) + return ret0, ret1 +} + +// AssignPet indicates an expected call of AssignPet. +func (mr *MockServiceMockRecorder) AssignPet(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AssignPet", reflect.TypeOf((*MockService)(nil).AssignPet), arg0) +} + // Delete mocks base method. -func (m *MockService) Delete(id string) (bool, *dto.ResponseErr) { +func (m *MockService) Delete(arg0 string) (*dto.DeleteImageResponse, *dto.ResponseErr) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", id) - ret0, _ := ret[0].(bool) + ret := m.ctrl.Call(m, "Delete", arg0) + ret0, _ := ret[0].(*dto.DeleteImageResponse) ret1, _ := ret[1].(*dto.ResponseErr) return ret0, ret1 } // Delete indicates an expected call of Delete. -func (mr *MockServiceMockRecorder) Delete(id interface{}) *gomock.Call { +func (mr *MockServiceMockRecorder) Delete(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockService)(nil).Delete), id) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockService)(nil).Delete), arg0) } // FindByPetId mocks base method. -func (m *MockService) FindByPetId(id string) ([]*v1.Image, *dto.ResponseErr) { +func (m *MockService) FindByPetId(arg0 string) ([]*dto.ImageResponse, *dto.ResponseErr) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindByPetId", id) - ret0, _ := ret[0].([]*v1.Image) + ret := m.ctrl.Call(m, "FindByPetId", arg0) + ret0, _ := ret[0].([]*dto.ImageResponse) ret1, _ := ret[1].(*dto.ResponseErr) return ret0, ret1 } // FindByPetId indicates an expected call of FindByPetId. -func (mr *MockServiceMockRecorder) FindByPetId(id interface{}) *gomock.Call { +func (mr *MockServiceMockRecorder) FindByPetId(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindByPetId", reflect.TypeOf((*MockService)(nil).FindByPetId), id) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindByPetId", reflect.TypeOf((*MockService)(nil).FindByPetId), arg0) } // Upload mocks base method. -func (m *MockService) Upload(in *dto.ImageDto) (*v1.Image, *dto.ResponseErr) { +func (m *MockService) Upload(arg0 *dto.UploadImageRequest) (*dto.ImageResponse, *dto.ResponseErr) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Upload", in) - ret0, _ := ret[0].(*v1.Image) + ret := m.ctrl.Call(m, "Upload", arg0) + ret0, _ := ret[0].(*dto.ImageResponse) ret1, _ := ret[1].(*dto.ResponseErr) return ret0, ret1 } // Upload indicates an expected call of Upload. -func (mr *MockServiceMockRecorder) Upload(in interface{}) *gomock.Call { +func (mr *MockServiceMockRecorder) Upload(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Upload", reflect.TypeOf((*MockService)(nil).Upload), in) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Upload", reflect.TypeOf((*MockService)(nil).Upload), arg0) } diff --git a/src/mocks/service/like/like.mock.go b/src/mocks/service/like/like.mock.go new file mode 100644 index 0000000..1e05b67 --- /dev/null +++ b/src/mocks/service/like/like.mock.go @@ -0,0 +1,80 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./src/pkg/service/like/like.service.go + +// Package mock_like is a generated GoMock package. +package mock_like + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + dto "github.com/isd-sgcu/johnjud-gateway/src/app/dto" +) + +// MockService is a mock of Service interface. +type MockService struct { + ctrl *gomock.Controller + recorder *MockServiceMockRecorder +} + +// MockServiceMockRecorder is the mock recorder for MockService. +type MockServiceMockRecorder struct { + mock *MockService +} + +// NewMockService creates a new mock instance. +func NewMockService(ctrl *gomock.Controller) *MockService { + mock := &MockService{ctrl: ctrl} + mock.recorder = &MockServiceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockService) EXPECT() *MockServiceMockRecorder { + return m.recorder +} + +// Create mocks base method. +func (m *MockService) Create(arg0 *dto.CreateLikeRequest) (*dto.LikeResponse, *dto.ResponseErr) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", arg0) + ret0, _ := ret[0].(*dto.LikeResponse) + ret1, _ := ret[1].(*dto.ResponseErr) + return ret0, ret1 +} + +// Create indicates an expected call of Create. +func (mr *MockServiceMockRecorder) Create(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockService)(nil).Create), arg0) +} + +// Delete mocks base method. +func (m *MockService) Delete(arg0 string) (*dto.DeleteLikeResponse, *dto.ResponseErr) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", arg0) + ret0, _ := ret[0].(*dto.DeleteLikeResponse) + ret1, _ := ret[1].(*dto.ResponseErr) + return ret0, ret1 +} + +// Delete indicates an expected call of Delete. +func (mr *MockServiceMockRecorder) Delete(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockService)(nil).Delete), arg0) +} + +// FindByUserId mocks base method. +func (m *MockService) FindByUserId(arg0 string) ([]*dto.LikeResponse, *dto.ResponseErr) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FindByUserId", arg0) + ret0, _ := ret[0].([]*dto.LikeResponse) + ret1, _ := ret[1].(*dto.ResponseErr) + return ret0, ret1 +} + +// FindByUserId indicates an expected call of FindByUserId. +func (mr *MockServiceMockRecorder) FindByUserId(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindByUserId", reflect.TypeOf((*MockService)(nil).FindByUserId), arg0) +} diff --git a/src/mocks/service/pet/pet.mock.go b/src/mocks/service/pet/pet.mock.go index 1673704..35b82b0 100644 --- a/src/mocks/service/pet/pet.mock.go +++ b/src/mocks/service/pet/pet.mock.go @@ -40,18 +40,18 @@ func (m *MockService) EXPECT() *MockServiceMockRecorder { } // Adopt mocks base method. -func (m *MockService) Adopt(arg0 *dto.AdoptDto) (bool, *dto.ResponseErr) { +func (m *MockService) Adopt(arg0 string, arg1 *dto.AdoptByRequest) (*dto.AdoptByResponse, *dto.ResponseErr) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Adopt", arg0) - ret0, _ := ret[0].(bool) + ret := m.ctrl.Call(m, "Adopt", arg0, arg1) + ret0, _ := ret[0].(*dto.AdoptByResponse) ret1, _ := ret[1].(*dto.ResponseErr) return ret0, ret1 } // Adopt indicates an expected call of Adopt. -func (mr *MockServiceMockRecorder) Adopt(arg0 any) *gomock.Call { +func (mr *MockServiceMockRecorder) Adopt(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Adopt", reflect.TypeOf((*MockService)(nil).Adopt), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Adopt", reflect.TypeOf((*MockService)(nil).Adopt), arg0, arg1) } // ChangeView mocks base method. @@ -100,18 +100,18 @@ func (mr *MockServiceMockRecorder) Delete(arg0 any) *gomock.Call { } // FindAll mocks base method. -func (m *MockService) FindAll() ([]*dto.PetResponse, *dto.ResponseErr) { +func (m *MockService) FindAll(arg0 *dto.FindAllPetRequest) (*dto.FindAllPetResponse, *dto.ResponseErr) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindAll") - ret0, _ := ret[0].([]*dto.PetResponse) + ret := m.ctrl.Call(m, "FindAll", arg0) + ret0, _ := ret[0].(*dto.FindAllPetResponse) ret1, _ := ret[1].(*dto.ResponseErr) return ret0, ret1 } // FindAll indicates an expected call of FindAll. -func (mr *MockServiceMockRecorder) FindAll() *gomock.Call { +func (mr *MockServiceMockRecorder) FindAll(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindAll", reflect.TypeOf((*MockService)(nil).FindAll)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindAll", reflect.TypeOf((*MockService)(nil).FindAll), arg0) } // FindOne mocks base method. diff --git a/src/mocks/service/user/user.mock.go b/src/mocks/service/user/user.mock.go index daa4fdb..ba40d67 100644 --- a/src/mocks/service/user/user.mock.go +++ b/src/mocks/service/user/user.mock.go @@ -9,7 +9,6 @@ import ( gomock "github.com/golang/mock/gomock" dto "github.com/isd-sgcu/johnjud-gateway/src/app/dto" - v1 "github.com/isd-sgcu/johnjud-go-proto/johnjud/auth/user/v1" ) // MockService is a mock of Service interface. @@ -35,11 +34,26 @@ func (m *MockService) EXPECT() *MockServiceMockRecorder { return m.recorder } +// Delete mocks base method. +func (m *MockService) Delete(arg0 string) (*dto.DeleteUserResponse, *dto.ResponseErr) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", arg0) + ret0, _ := ret[0].(*dto.DeleteUserResponse) + ret1, _ := ret[1].(*dto.ResponseErr) + return ret0, ret1 +} + +// Delete indicates an expected call of Delete. +func (mr *MockServiceMockRecorder) Delete(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockService)(nil).Delete), arg0) +} + // FindOne mocks base method. -func (m *MockService) FindOne(arg0 string) (*v1.User, *dto.ResponseErr) { +func (m *MockService) FindOne(arg0 string) (*dto.User, *dto.ResponseErr) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "FindOne", arg0) - ret0, _ := ret[0].(*v1.User) + ret0, _ := ret[0].(*dto.User) ret1, _ := ret[1].(*dto.ResponseErr) return ret0, ret1 } @@ -51,10 +65,10 @@ func (mr *MockServiceMockRecorder) FindOne(arg0 interface{}) *gomock.Call { } // Update mocks base method. -func (m *MockService) Update(arg0 string, arg1 *dto.UpdateUserDto) (*v1.User, *dto.ResponseErr) { +func (m *MockService) Update(arg0 string, arg1 *dto.UpdateUserRequest) (*dto.User, *dto.ResponseErr) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Update", arg0, arg1) - ret0, _ := ret[0].(*v1.User) + ret0, _ := ret[0].(*dto.User) ret1, _ := ret[1].(*dto.ResponseErr) return ret0, ret1 } diff --git a/src/mocks/user/user.mock.go b/src/mocks/user/user.mock.go deleted file mode 100644 index 909dab8..0000000 --- a/src/mocks/user/user.mock.go +++ /dev/null @@ -1,76 +0,0 @@ -package user - -import ( - "context" - - "github.com/isd-sgcu/johnjud-gateway/src/app/dto" - user_proto "github.com/isd-sgcu/johnjud-go-proto/johnjud/auth/user/v1" - "github.com/stretchr/testify/mock" - "google.golang.org/grpc" -) - -type ServiceMock struct { - mock.Mock -} - -func (s *ServiceMock) FindOne(id string) (result *user_proto.User, err *dto.ResponseErr) { - args := s.Called(id) - - if args.Get(0) != nil { - result = args.Get(0).(*user_proto.User) - } - - if args.Get(1) != nil { - err = args.Get(1).(*dto.ResponseErr) - } - - return -} - -func (s *ServiceMock) Update(id string, in *dto.UpdateUserDto) (result *user_proto.User, err *dto.ResponseErr) { - args := s.Called(id, in) - - if args.Get(0) != nil { - result = args.Get(0).(*user_proto.User) - } - - if args.Get(1) != nil { - err = args.Get(1).(*dto.ResponseErr) - } - - return -} - -type ClientMock struct { - mock.Mock -} - -func (c *ClientMock) FindOne(_ context.Context, in *user_proto.FindOneUserRequest, _ ...grpc.CallOption) (res *user_proto.FindOneUserResponse, err error) { - args := c.Called(in) - - if args.Get(0) != nil { - res = args.Get(0).(*user_proto.FindOneUserResponse) - } - - return res, args.Error(1) -} - -func (c *ClientMock) Update(_ context.Context, in *user_proto.UpdateUserRequest, _ ...grpc.CallOption) (res *user_proto.UpdateUserResponse, err error) { - args := c.Called(in) - - if args.Get(0) != nil { - res = args.Get(0).(*user_proto.UpdateUserResponse) - } - - return res, args.Error(1) -} - -func (c *ClientMock) Delete(_ context.Context, in *user_proto.DeleteUserRequest, _ ...grpc.CallOption) (res *user_proto.DeleteUserResponse, err error) { - args := c.Called(in) - - if args.Get(0) != nil { - res = args.Get(0).(*user_proto.DeleteUserResponse) - } - - return res, args.Error(1) -} diff --git a/src/pkg/service/image/image.service.go b/src/pkg/service/image/image.service.go index 4b68d29..e7ff7a1 100644 --- a/src/pkg/service/image/image.service.go +++ b/src/pkg/service/image/image.service.go @@ -2,11 +2,11 @@ package image import ( "github.com/isd-sgcu/johnjud-gateway/src/app/dto" - proto "github.com/isd-sgcu/johnjud-go-proto/johnjud/file/image/v1" ) type Service interface { - FindByPetId(string) ([]*proto.Image, *dto.ResponseErr) - Upload(*dto.ImageDto) (*proto.Image, *dto.ResponseErr) - Delete(string) (bool, *dto.ResponseErr) + FindByPetId(string) ([]*dto.ImageResponse, *dto.ResponseErr) + Upload(*dto.UploadImageRequest) (*dto.ImageResponse, *dto.ResponseErr) + Delete(string) (*dto.DeleteImageResponse, *dto.ResponseErr) + AssignPet(*dto.AssignPetRequest) (*dto.AssignPetResponse, *dto.ResponseErr) } diff --git a/src/pkg/service/like/like.service.go b/src/pkg/service/like/like.service.go new file mode 100644 index 0000000..e260a4b --- /dev/null +++ b/src/pkg/service/like/like.service.go @@ -0,0 +1,9 @@ +package like + +import "github.com/isd-sgcu/johnjud-gateway/src/app/dto" + +type Service interface { + FindByUserId(string) ([]*dto.LikeResponse, *dto.ResponseErr) + Create(*dto.CreateLikeRequest) (*dto.LikeResponse, *dto.ResponseErr) + Delete(string) (*dto.DeleteLikeResponse, *dto.ResponseErr) +} diff --git a/src/pkg/service/pet/pet.service.go b/src/pkg/service/pet/pet.service.go index e538fb1..fbbe5bc 100644 --- a/src/pkg/service/pet/pet.service.go +++ b/src/pkg/service/pet/pet.service.go @@ -5,11 +5,11 @@ import ( ) type Service interface { - FindAll() ([]*dto.PetResponse, *dto.ResponseErr) + FindAll(*dto.FindAllPetRequest) (*dto.FindAllPetResponse, *dto.ResponseErr) FindOne(string) (*dto.PetResponse, *dto.ResponseErr) Create(*dto.CreatePetRequest) (*dto.PetResponse, *dto.ResponseErr) 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) } diff --git a/src/pkg/service/user/user.service.go b/src/pkg/service/user/user.service.go index 1002d1d..731063b 100644 --- a/src/pkg/service/user/user.service.go +++ b/src/pkg/service/user/user.service.go @@ -2,10 +2,10 @@ package user import ( "github.com/isd-sgcu/johnjud-gateway/src/app/dto" - user_proto "github.com/isd-sgcu/johnjud-go-proto/johnjud/auth/user/v1" ) type Service interface { - FindOne(string) (*user_proto.User, *dto.ResponseErr) - Update(string, *dto.UpdateUserDto) (*user_proto.User, *dto.ResponseErr) + FindOne(string) (*dto.User, *dto.ResponseErr) + Update(string, *dto.UpdateUserRequest) (*dto.User, *dto.ResponseErr) + Delete(string) (*dto.DeleteUserResponse, *dto.ResponseErr) }