Skip to content

Commit

Permalink
Merge pull request #10 from isd-sgcu/JOH-84/image-findall-deletebypetid
Browse files Browse the repository at this point in the history
[JOH-84] Image Service's FindAll, DeleteByPetId
  • Loading branch information
bookpanda authored Feb 14, 2024
2 parents adb8a40 + 39627f7 commit 74fdfb7
Show file tree
Hide file tree
Showing 10 changed files with 308 additions and 14 deletions.
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

0 comments on commit 74fdfb7

Please sign in to comment.