Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[JOH-84] Image Service's FindAll, DeleteByPetId #10

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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions client/bucket/bucket.client.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,30 @@ func (c *Client) Delete(objectKey string) error {
return nil
}

func (c *Client) DeleteMany(objectKeys []string) error {
ctx := context.Background()
ctx, cancel := context.WithTimeout(ctx, 50*time.Second)
defer cancel()

opts := minio.RemoveObjectOptions{
GovernanceBypass: true,
}
for _, objectKey := range objectKeys {
err := c.minio.RemoveObject(context.Background(), c.conf.BucketName, objectKey, opts)
if err != nil {
log.Error().
Err(err).
Str("service", "file").
Str("module", "bucket client").
Msgf("Couldn't delete object from bucket %v:%v.", c.conf.BucketName, objectKey)

return errors.Wrap(err, "Error while deleting the object")
}
}

return nil
}

func (c *Client) getURL(objectKey string) string {
return "https://" + c.conf.Endpoint + "/" + c.conf.BucketName + "/" + objectKey
}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ require (
github.com/go-faker/faker/v4 v4.2.0
github.com/golang/mock v1.6.0
github.com/google/uuid v1.5.0
github.com/isd-sgcu/johnjud-go-proto v0.5.0
github.com/isd-sgcu/johnjud-go-proto v0.6.0
github.com/minio/minio-go/v7 v7.0.66
github.com/pkg/errors v0.9.1
github.com/rs/zerolog v1.31.0
github.com/spf13/viper v1.18.1
github.com/stretchr/testify v1.8.4
google.golang.org/grpc v1.60.1
google.golang.org/grpc v1.61.0
gorm.io/driver/postgres v1.5.4
gorm.io/gorm v1.25.5
)
Expand Down
12 changes: 6 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
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.5.0 h1:GgqRzWjya5p1yhfU/kpX8i4WL42+qT2TkyXZmssH6B4=
github.com/isd-sgcu/johnjud-go-proto v0.5.0/go.mod h1:1OK6aiCgtXQiLhxp0r6iLEejYIRpckWQZDrCZ9Trbo4=
github.com/isd-sgcu/johnjud-go-proto v0.6.0 h1:sOWGjsqXwzpSaweSlNqPlttExZryp8mV76ob95LppjM=
github.com/isd-sgcu/johnjud-go-proto v0.6.0/go.mod h1:Yfajs+jSTcuVAKK9xLcVbZkUvCBO3exZV8bOGdelvW0=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
Expand Down Expand Up @@ -154,8 +154,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc=
google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU=
google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0=
google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
Expand Down
7 changes: 7 additions & 0 deletions internal/repository/image/image.repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ func NewRepository(db *gorm.DB) image.Repository {
return &repositoryImpl{db: db}
}

func (r *repositoryImpl) FindAll(result *[]*model.Image) error {
return r.db.Model(&model.Image{}).Find(result).Error
}

func (r *repositoryImpl) FindOne(id string, result *model.Image) error {
return r.db.Model(&model.Image{}).First(result, "id = ?", id).Error
}
Expand All @@ -33,3 +37,6 @@ func (r *repositoryImpl) Update(id string, in *model.Image) error {
func (r *repositoryImpl) Delete(id string) error {
return r.db.Where("id = ?", id).Delete(&model.Image{}).Error
}
func (r *repositoryImpl) DeleteMany(ids []string) error {
return r.db.Delete(&model.Image{}, ids).Error
}
78 changes: 78 additions & 0 deletions internal/service/image/image.service.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,22 @@ func NewService(client bucket.Client, repository image.Repository, random utils.
}
}

func (s *serviceImpl) FindAll(_ context.Context, req *proto.FindAllImageRequest) (res *proto.FindAllImageResponse, err error) {
var images []*model.Image

err = s.repository.FindAll(&images)
if err != nil {
log.Error().Err(err).
Str("service", "image").
Str("module", "find all").
Msg("Error finding all images")

return nil, status.Error(codes.Internal, constant.InternalServerErrorMessage)
}

return &proto.FindAllImageResponse{Images: RawToDtoList(&images)}, nil
}

func (s *serviceImpl) FindByPetId(_ context.Context, req *proto.FindImageByPetIdRequest) (res *proto.FindImageByPetIdResponse, err error) {
var images []*model.Image

Expand Down Expand Up @@ -190,6 +206,50 @@ func (s *serviceImpl) Delete(_ context.Context, req *proto.DeleteImageRequest) (
return &proto.DeleteImageResponse{Success: true}, nil
}

func (s *serviceImpl) DeleteByPetId(_ context.Context, req *proto.DeleteByPetIdRequest) (res *proto.DeleteByPetIdResponse, err error) {
var images []*model.Image

err = s.repository.FindByPetId(req.PetId, &images)
if err != nil {
log.Error().Err(err).
Str("service", "image").
Str("module", "delete by pet id").
Str("pet id", req.PetId).
Msg("Error finding image from repo")
if err == gorm.ErrRecordNotFound {
return nil, status.Error(codes.NotFound, constant.ImageNotFoundErrorMessage)
}

return nil, status.Error(codes.Internal, constant.InternalServerErrorMessage)
}

imageObjectKeys := ExtractImageObjectKeys(images)
err = s.client.DeleteMany(imageObjectKeys)
if err != nil {
log.Error().Err(err).
Str("service", "image").
Str("module", "delete by pet id").
Interface("image object keys", imageObjectKeys).
Msg(constant.DeleteFromBucketErrorMessage)

return nil, status.Error(codes.Internal, constant.DeleteFromBucketErrorMessage)
}

imageIds := ExtractImageIds(images)
err = s.repository.DeleteMany(imageIds)
if err != nil {
log.Error().Err(err).
Str("service", "image").
Str("module", "delete by pet id").
Interface("image ids", imageIds).
Msg(constant.DeleteImageErrorMessage)

return nil, status.Error(codes.Internal, constant.DeleteImageErrorMessage)
}

return &proto.DeleteByPetIdResponse{Success: true}, nil
}

func DtoToRaw(in *proto.Image) (result *model.Image, err error) {
var id uuid.UUID
if in.Id != "" {
Expand Down Expand Up @@ -253,3 +313,21 @@ func RawToDto(in *model.Image) *proto.Image {
ObjectKey: in.ObjectKey,
}
}

func ExtractImageIds(in []*model.Image) []string {
var imageIds []string
for _, image := range in {
imageIds = append(imageIds, image.ID.String())
}

return imageIds
}

func ExtractImageObjectKeys(in []*model.Image) []string {
var imageObjectKeys []string
for _, image := range in {
imageObjectKeys = append(imageObjectKeys, image.ObjectKey)
}

return imageObjectKeys
}
153 changes: 153 additions & 0 deletions internal/service/image/image.service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,17 @@ type ImageServiceTest struct {
id uuid.UUID
petId uuid.UUID
objectKey string
objectKeys []string
imageUrl string
randomString string
objectKeyWithRandom string
findAllReq *proto.FindAllImageRequest
findReq *proto.FindImageByPetIdRequest
uploadReq *proto.UploadImageRequest
assignReq *proto.AssignPetRequest
deleteReq *proto.DeleteImageRequest
deleteByPetIdReq *proto.DeleteByPetIdRequest
imageIds []string
imageProto *proto.Image
image *model.Image
images []*model.Image
Expand All @@ -53,6 +57,7 @@ func (t *ImageServiceTest) SetupTest() {
t.randomString = "random"
t.objectKeyWithRandom = t.randomString + "_" + t.objectKey

t.findAllReq = &proto.FindAllImageRequest{}
t.findReq = &proto.FindImageByPetIdRequest{
PetId: t.petId.String(),
}
Expand All @@ -68,6 +73,9 @@ func (t *ImageServiceTest) SetupTest() {
t.deleteReq = &proto.DeleteImageRequest{
Id: t.id.String(),
}
t.deleteByPetIdReq = &proto.DeleteByPetIdRequest{
PetId: t.petId.String(),
}
t.imageProto = &proto.Image{
Id: t.id.String(),
PetId: t.petId.String(),
Expand Down Expand Up @@ -106,6 +114,62 @@ func (t *ImageServiceTest) SetupTest() {
ObjectKey: faker.Name(),
},
}
t.objectKeys = []string{t.images[0].ObjectKey, t.images[1].ObjectKey}
t.imageIds = []string{t.images[0].ID.String(), t.images[1].ID.String()}
}

func (t *ImageServiceTest) TestFindAllSuccess() {
expected := &proto.FindAllImageResponse{
Images: []*proto.Image{
{
Id: t.images[0].ID.String(),
PetId: t.images[0].PetID.String(),
ImageUrl: t.images[0].ImageUrl,
ObjectKey: t.images[0].ObjectKey,
},
{
Id: t.images[1].ID.String(),
PetId: t.images[1].PetID.String(),
ImageUrl: t.images[1].ImageUrl,
ObjectKey: t.images[1].ObjectKey,
},
},
}
var images []*model.Image

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

imageRepo := &mock_image.ImageRepositoryMock{}
bucketClient := mock_bucket.NewMockClient(controller)
randomUtils := &mock_random.RandomUtilMock{}
imageRepo.On("FindAll", &images).Return(&t.images, nil)

imageService := NewService(bucketClient, imageRepo, randomUtils)
actual, err := imageService.FindAll(context.Background(), t.findAllReq)

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

func (t *ImageServiceTest) TestFindAllInternalErr() {
expected := status.Error(codes.Internal, constant.InternalServerErrorMessage)
var images []*model.Image

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

imageRepo := &mock_image.ImageRepositoryMock{}
bucketClient := mock_bucket.NewMockClient(controller)
randomUtils := &mock_random.RandomUtilMock{}
imageRepo.On("FindAll", &images).Return(nil, errors.New("Error finding image in db"))

imageService := NewService(bucketClient, imageRepo, randomUtils)
actual, err := imageService.FindAll(context.Background(), t.findAllReq)

status, ok := status.FromError(err)
assert.True(t.T(), ok)
assert.Nil(t.T(), actual)
assert.Equal(t.T(), codes.Internal, status.Code())
assert.Equal(t.T(), expected.Error(), err.Error())
}

func (t *ImageServiceTest) TestFindByPetIdSuccess() {
Expand Down Expand Up @@ -593,3 +657,92 @@ func (t *ImageServiceTest) TestDeleteInternalErr() {
assert.Equal(t.T(), codes.Internal, status.Code())
assert.Equal(t.T(), expected.Error(), err.Error())
}

func (t *ImageServiceTest) TestDeleteByPetIdSuccess() {
expected := &proto.DeleteByPetIdResponse{
Success: true,
}
var images []*model.Image

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

imageRepo := &mock_image.ImageRepositoryMock{}
bucketClient := mock_bucket.NewMockClient(controller)
randomUtils := &mock_random.RandomUtilMock{}
imageRepo.On("FindByPetId", t.petId.String(), &images).Return(&t.images, nil)
imageRepo.On("DeleteMany", t.imageIds).Return(nil)
bucketClient.EXPECT().DeleteMany(t.objectKeys).Return(nil)

imageService := NewService(bucketClient, imageRepo, randomUtils)
actual, err := imageService.DeleteByPetId(context.Background(), t.deleteByPetIdReq)

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

func (t *ImageServiceTest) TestDeleteByPetIdBucketFailed() {
expected := status.Error(codes.Internal, constant.DeleteFromBucketErrorMessage)
var images []*model.Image

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

imageRepo := &mock_image.ImageRepositoryMock{}
bucketClient := mock_bucket.NewMockClient(controller)
randomUtils := &mock_random.RandomUtilMock{}
imageRepo.On("FindByPetId", t.petId.String(), &images).Return(&t.images, nil)
imageRepo.On("DeleteMany", t.imageIds).Return(nil)
bucketClient.EXPECT().DeleteMany(t.objectKeys).Return(errors.New("Error deleting from bucket client"))

imageService := NewService(bucketClient, imageRepo, randomUtils)
actual, err := imageService.DeleteByPetId(context.Background(), t.deleteByPetIdReq)

status, ok := status.FromError(err)
assert.True(t.T(), ok)
assert.Nil(t.T(), actual)
assert.Equal(t.T(), codes.Internal, status.Code())
assert.Equal(t.T(), expected.Error(), err.Error())
}

func (t *ImageServiceTest) TestDeleteByPetIdNotFound() {
expected := status.Error(codes.NotFound, constant.ImageNotFoundErrorMessage)
var images []*model.Image

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

imageRepo := &mock_image.ImageRepositoryMock{}
bucketClient := mock_bucket.NewMockClient(controller)
randomUtils := &mock_random.RandomUtilMock{}
imageRepo.On("FindByPetId", t.petId.String(), &images).Return(nil, gorm.ErrRecordNotFound)

imageService := NewService(bucketClient, imageRepo, randomUtils)
actual, err := imageService.DeleteByPetId(context.Background(), t.deleteByPetIdReq)

status, ok := status.FromError(err)
assert.True(t.T(), ok)
assert.Nil(t.T(), actual)
assert.Equal(t.T(), codes.NotFound, status.Code())
assert.Equal(t.T(), expected.Error(), err.Error())
}

func (t *ImageServiceTest) TestDeleteByPetIdInternalErr() {
expected := status.Error(codes.Internal, constant.DeleteImageErrorMessage)
var images []*model.Image

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

imageRepo := &mock_image.ImageRepositoryMock{}
bucketClient := mock_bucket.NewMockClient(controller)
randomUtils := &mock_random.RandomUtilMock{}
imageRepo.On("FindByPetId", t.petId.String(), &images).Return(&t.images, nil)
imageRepo.On("DeleteMany", t.imageIds).Return(errors.New(constant.DeleteImageErrorMessage))
bucketClient.EXPECT().DeleteMany(t.objectKeys).Return(nil)

imageService := NewService(bucketClient, imageRepo, randomUtils)
actual, err := imageService.DeleteByPetId(context.Background(), t.deleteByPetIdReq)

status, ok := status.FromError(err)
assert.True(t.T(), ok)
assert.Nil(t.T(), actual)
assert.Equal(t.T(), codes.Internal, status.Code())
assert.Equal(t.T(), expected.Error(), err.Error())
}
Loading
Loading