diff --git a/go.mod b/go.mod index ade5ec4..665bb29 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ toolchain go1.21.5 require ( github.com/bxcodec/faker/v3 v3.8.1 github.com/google/uuid v1.5.0 - github.com/isd-sgcu/johnjud-go-proto v0.0.9 + github.com/isd-sgcu/johnjud-go-proto v0.2.4 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 fc7670b..7886bd7 100644 --- a/go.sum +++ b/go.sum @@ -22,6 +22,8 @@ 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.0.9 h1:cFfZ2JSpW0jg94Iv5zHQJGnoekj0eCQe42SJaTpnp3c= github.com/isd-sgcu/johnjud-go-proto v0.0.9/go.mod h1:1OK6aiCgtXQiLhxp0r6iLEejYIRpckWQZDrCZ9Trbo4= +github.com/isd-sgcu/johnjud-go-proto v0.2.4 h1:amYofKCZGMKc+VQARmsZSPgmpxEJwQjv6VfbCxI9wLw= +github.com/isd-sgcu/johnjud-go-proto v0.2.4/go.mod h1:1OK6aiCgtXQiLhxp0r6iLEejYIRpckWQZDrCZ9Trbo4= 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= diff --git a/src/app/model/pet/pet.model.go b/src/app/model/pet/pet.model.go index 3c88a37..8a33e49 100644 --- a/src/app/model/pet/pet.model.go +++ b/src/app/model/pet/pet.model.go @@ -15,10 +15,10 @@ type Pet struct { Habit string `json:"habit" gorm:"mediumtext"` Caption string `json:"caption" gorm:"mediumtext"` Status pet.Status `json:"status" gorm:"mediumtext" example:"findhome"` - IsSterile bool `json:"is_sterile"` - IsVaccinated bool `json:"is_vaccine"` - IsVisible bool `json:"is_visible"` - IsClubPet bool `json:"is_club_pet"` + IsSterile *bool `json:"is_sterile"` + IsVaccinated *bool `json:"is_vaccine"` + IsVisible *bool `json:"is_visible"` + IsClubPet *bool `json:"is_club_pet"` Background string `json:"background" gorm:"tinytext"` Address string `json:"address" gorm:"tinytext"` Contact string `json:"contact" gorm:"tinytext"` diff --git a/src/app/repository/pet/pet.repository.go b/src/app/repository/pet/pet.repository.go index 2ee80f1..ff20ff2 100644 --- a/src/app/repository/pet/pet.repository.go +++ b/src/app/repository/pet/pet.repository.go @@ -1,6 +1,7 @@ package pet import ( + "github.com/google/uuid" "github.com/isd-sgcu/johnjud-backend/src/app/model/pet" "gorm.io/gorm" ) @@ -18,7 +19,15 @@ func (r *Repository) FindAll(result *[]*pet.Pet) error { } func (r *Repository) FindOne(id string, result *pet.Pet) error { - return r.db.Model(&pet.Pet{}).Find(result, "id = ?", id).Error + if err := r.db.Model(&pet.Pet{}).Find(result, "id = ?", id).Error; err != nil { + return err + } + + if result.ID == uuid.Nil { + return gorm.ErrRecordNotFound + } + + return nil } func (r *Repository) Create(in *pet.Pet) error { diff --git a/src/app/service/pet/pet.service.go b/src/app/service/pet/pet.service.go index 552f81b..cd7b507 100644 --- a/src/app/service/pet/pet.service.go +++ b/src/app/service/pet/pet.service.go @@ -54,7 +54,7 @@ func (s *Service) Delete(ctx context.Context, req *proto.DeletePetRequest) (*pro func (s *Service) Update(_ context.Context, req *proto.UpdatePetRequest) (res *proto.UpdatePetResponse, err error) { raw, err := DtoToRaw(req.Pet) if err != nil { - return nil, status.Error(codes.Internal, "error converting dto to raw") + return nil, status.Error(codes.InvalidArgument, "error converting dto to raw") } err = s.repository.Update(req.Pet.Id, raw) @@ -62,13 +62,9 @@ func (s *Service) Update(_ context.Context, req *proto.UpdatePetRequest) (res *p return nil, status.Error(codes.NotFound, "pet not found") } - images, err := s.imageService.FindByPetId(req.Pet.Id) - if err != nil { - return nil, status.Error(codes.Internal, "error querying image service") - } - imageUrls := ExtractImageUrls(images) - - return &proto.UpdatePetResponse{Pet: RawToDto(raw, imageUrls)}, nil + var images []*image_proto.Image + // images, err := s.imageService.FindByPetId(req.Pet.Id) + return &proto.UpdatePetResponse{Pet: RawToDto(raw, images)}, nil } func (s *Service) ChangeView(_ context.Context, req *proto.ChangeViewPetRequest) (res *proto.ChangeViewPetResponse, err error) { @@ -78,9 +74,9 @@ func (s *Service) ChangeView(_ context.Context, req *proto.ChangeViewPetRequest) } pet, err := DtoToRaw(petData.Pet) if err != nil { - return nil, status.Error(codes.Internal, "error converting dto to raw") + return nil, status.Error(codes.InvalidArgument, "error converting dto to raw") } - pet.IsVisible = req.Visible + pet.IsVisible = &req.Visible err = s.repository.Update(req.Id, pet) if err != nil { @@ -92,7 +88,6 @@ func (s *Service) ChangeView(_ context.Context, req *proto.ChangeViewPetRequest) func (s *Service) FindAll(_ context.Context, req *proto.FindAllPetRequest) (res *proto.FindAllPetResponse, err error) { var pets []*pet.Pet - var imageUrlsList [][]string err = s.repository.FindAll(&pets) if err != nil { @@ -100,21 +95,13 @@ func (s *Service) FindAll(_ context.Context, req *proto.FindAllPetRequest) (res return nil, status.Error(codes.Unavailable, "Internal error") } - for _, pet := range pets { - images, err := s.imageService.FindByPetId(pet.ID.String()) - if err != nil { - return nil, status.Error(codes.Internal, "error querying image service") - } - imageUrls := ExtractImageUrls(images) - imageUrlsList = append(imageUrlsList, imageUrls) - } - - petWithImageUrls, err := RawToDtoList(&pets, imageUrlsList) + imagesList := make([][]*image_proto.Image, len(pets)) + petWithImages, err := RawToDtoList(&pets, imagesList) if err != nil { - return nil, status.Error(codes.Internal, "error converting raw to dto list") + return nil, status.Error(codes.InvalidArgument, "error converting raw to dto list") } - return &proto.FindAllPetResponse{Pets: petWithImageUrls}, nil + return &proto.FindAllPetResponse{Pets: petWithImages}, nil } func (s Service) FindOne(_ context.Context, req *proto.FindOnePetRequest) (res *proto.FindOnePetResponse, err error) { @@ -127,29 +114,24 @@ func (s Service) FindOne(_ context.Context, req *proto.FindOnePetRequest) (res * return nil, status.Error(codes.NotFound, err.Error()) } - images, err := s.imageService.FindByPetId(req.Id) - if err != nil { - return nil, status.Error(codes.Internal, "error querying image service") - } - imageUrls := ExtractImageUrls(images) - - return &proto.FindOnePetResponse{Pet: RawToDto(&pet, imageUrls)}, err + var images []*image_proto.Image + return &proto.FindOnePetResponse{Pet: RawToDto(&pet, images)}, err } func (s *Service) Create(_ context.Context, req *proto.CreatePetRequest) (res *proto.CreatePetResponse, err error) { raw, err := DtoToRaw(req.Pet) if err != nil { - return nil, status.Error(codes.Internal, "error converting dto to raw: "+err.Error()) + return nil, status.Error(codes.InvalidArgument, "error converting dto to raw: "+err.Error()) } - imgUrls := []string{} - err = s.repository.Create(raw) if err != nil { return nil, status.Error(codes.Internal, "failed to create pet") } - return &proto.CreatePetResponse{Pet: RawToDto(raw, imgUrls)}, nil + var images []*image_proto.Image + + return &proto.CreatePetResponse{Pet: RawToDto(raw, images)}, nil } func (s *Service) AdoptPet(ctx context.Context, req *proto.AdoptPetRequest) (res *proto.AdoptPetResponse, err error) { @@ -159,7 +141,7 @@ func (s *Service) AdoptPet(ctx context.Context, req *proto.AdoptPetRequest) (res } pet, err := DtoToRaw(dtoPet.Pet) if err != nil { - return nil, status.Error(codes.Internal, "error converting dto to raw") + return nil, status.Error(codes.InvalidArgument, "error converting dto to raw") } pet.AdoptBy = req.UserId @@ -171,39 +153,53 @@ func (s *Service) AdoptPet(ctx context.Context, req *proto.AdoptPetRequest) (res return &proto.AdoptPetResponse{Success: true}, nil } -func RawToDtoList(in *[]*pet.Pet, imageUrls [][]string) ([]*proto.Pet, error) { +func RawToDtoList(in *[]*pet.Pet, imagesList [][]*image_proto.Image) ([]*proto.Pet, error) { var result []*proto.Pet - if len(*in) != len(imageUrls) { - return nil, errors.New("length of in and imageUrls have to be the same") + if len(*in) != len(imagesList) { + return nil, status.Error(codes.InvalidArgument, "length of in and imagesList have to be the same") } for i, e := range *in { - result = append(result, RawToDto(e, imageUrls[i])) + result = append(result, RawToDto(e, imagesList[i])) } return result, nil } -func RawToDto(in *pet.Pet, imgUrl []string) *proto.Pet { - return &proto.Pet{ - Id: in.ID.String(), - Type: in.Type, - Species: in.Species, - Name: in.Name, - Birthdate: in.Birthdate, - Gender: proto.Gender(in.Gender), - Habit: in.Habit, - Caption: in.Caption, - Status: proto.PetStatus(in.Status), - ImageUrls: imgUrl, - IsSterile: in.IsSterile, - IsVaccinated: in.IsVaccinated, - IsVisible: in.IsVisible, - IsClubPet: in.IsClubPet, - Background: in.Background, - Address: in.Address, - Contact: in.Contact, - AdoptBy: in.AdoptBy, +func RawToDto(in *pet.Pet, images []*image_proto.Image) *proto.Pet { + pet := &proto.Pet{ + Id: in.ID.String(), + Type: in.Type, + Species: in.Species, + Name: in.Name, + Birthdate: in.Birthdate, + Gender: proto.Gender(in.Gender), + Habit: in.Habit, + Caption: in.Caption, + Status: proto.PetStatus(in.Status), + Images: images, + Background: in.Background, + Address: in.Address, + Contact: in.Contact, + AdoptBy: in.AdoptBy, + } + + if in.IsClubPet != nil { + pet.IsClubPet = *in.IsClubPet + } + + if in.IsSterile != nil { + pet.IsSterile = *in.IsSterile } + + if in.IsVaccinated != nil { + pet.IsVaccinated = *in.IsVaccinated + } + + if in.IsVisible != nil { + pet.IsVisible = *in.IsVisible + } + + return pet } func DtoToRaw(in *proto.Pet) (res *pet.Pet, err error) { @@ -232,30 +228,44 @@ func DtoToRaw(in *proto.Pet) (res *pet.Pet, err error) { status = petConst.FINDHOME } - return &pet.Pet{ + pet := &pet.Pet{ Base: model.Base{ ID: id, CreatedAt: time.Time{}, UpdatedAt: time.Time{}, DeletedAt: gorm.DeletedAt{}, }, - Type: in.Type, - Species: in.Species, - Name: in.Name, - Birthdate: in.Birthdate, - Gender: gender, - Habit: in.Habit, - Caption: in.Caption, - Status: status, - IsSterile: in.IsSterile, - IsVaccinated: in.IsVaccinated, - IsVisible: in.IsVisible, - IsClubPet: in.IsClubPet, - Background: in.Background, - Address: in.Address, - Contact: in.Contact, - AdoptBy: in.AdoptBy, - }, nil + Type: in.Type, + Species: in.Species, + Name: in.Name, + Birthdate: in.Birthdate, + Gender: gender, + Habit: in.Habit, + Caption: in.Caption, + Status: status, + Background: in.Background, + Address: in.Address, + Contact: in.Contact, + AdoptBy: in.AdoptBy, + } + + if &in.IsSterile != nil { + pet.IsSterile = &in.IsSterile + } + + if &in.IsVaccinated != nil { + pet.IsVaccinated = &in.IsVaccinated + } + + if &in.IsVisible != nil { + pet.IsVisible = &in.IsVisible + } + + if &in.IsClubPet != nil { + pet.IsClubPet = &in.IsClubPet + } + + return pet, nil } func ExtractImageUrls(in []*image_proto.Image) []string { diff --git a/src/app/service/pet/pet.service_test.go b/src/app/service/pet/pet.service_test.go index 04d36b9..8b0329a 100644 --- a/src/app/service/pet/pet.service_test.go +++ b/src/app/service/pet/pet.service_test.go @@ -15,6 +15,7 @@ import ( "github.com/isd-sgcu/johnjud-backend/src/app/model" "github.com/isd-sgcu/johnjud-backend/src/app/model/pet" + "github.com/isd-sgcu/johnjud-backend/src/app/utils" proto "github.com/isd-sgcu/johnjud-go-proto/johnjud/backend/pet/v1" img_proto "github.com/isd-sgcu/johnjud-go-proto/johnjud/file/image/v1" @@ -66,26 +67,17 @@ func (t *PetServiceTest) SetupTest() { Habit: faker.Paragraph(), Caption: faker.Paragraph(), Status: petConst.Status(rand.Intn(1) + 1), - IsSterile: true, - IsVaccinated: true, - IsVisible: true, - IsClubPet: true, + IsSterile: utils.BoolAddr(true), + IsVaccinated: utils.BoolAddr(true), + IsVisible: utils.BoolAddr(true), + IsClubPet: utils.BoolAddr(true), Background: faker.Paragraph(), Address: faker.Paragraph(), Contact: faker.Paragraph(), AdoptBy: "", } var images []*img_proto.Image - var imageUrls []string - for i := 0; i < 3; i++ { - url := faker.URL() - images = append(images, &img_proto.Image{ - Id: faker.UUIDDigit(), - PetId: pet.ID.String(), - ImageUrl: url, - }) - imageUrls = append(imageUrls, url) - } + imageUrls := []string{} t.ImagesList = append(t.ImagesList, images) t.ImageUrlsList = append(t.ImageUrlsList, imageUrls) pets = append(pets, pet) @@ -94,12 +86,6 @@ func (t *PetServiceTest) SetupTest() { t.Pets = pets t.Pet = pets[0] - for _, images := range t.ImagesList { - for _, image := range images { - t.ImageUrls = append(t.ImageUrls, image.ImageUrl) - } - } - t.Images = t.ImagesList[0] t.ImageUrls = t.ImageUrlsList[0] @@ -113,14 +99,14 @@ func (t *PetServiceTest) SetupTest() { Habit: t.Pet.Habit, Caption: t.Pet.Caption, Status: proto.PetStatus(t.Pet.Status), - IsSterile: t.Pet.IsSterile, - IsVaccinated: t.Pet.IsVaccinated, - IsVisible: t.Pet.IsVisible, - IsClubPet: t.Pet.IsClubPet, + IsSterile: *t.Pet.IsSterile, + IsVaccinated: *t.Pet.IsVaccinated, + IsVisible: *t.Pet.IsVisible, + IsClubPet: *t.Pet.IsClubPet, Background: t.Pet.Background, Address: t.Pet.Address, Contact: t.Pet.Contact, - ImageUrls: t.ImageUrls, + Images: t.Images, } t.UpdatePet = &pet.Pet{ @@ -164,7 +150,7 @@ func (t *PetServiceTest) SetupTest() { Status: t.Pet.Status, IsSterile: t.Pet.IsSterile, IsVaccinated: t.Pet.IsVaccinated, - IsVisible: false, + IsVisible: utils.BoolAddr(false), IsClubPet: t.Pet.IsClubPet, Background: t.Pet.Background, Address: t.Pet.Address, @@ -181,11 +167,11 @@ func (t *PetServiceTest) SetupTest() { Habit: t.Pet.Habit, Caption: t.Pet.Caption, Status: proto.PetStatus(t.Pet.Status), - ImageUrls: t.ImageUrls, - IsSterile: t.Pet.IsSterile, - IsVaccinated: t.Pet.IsVaccinated, - IsVisible: t.Pet.IsVaccinated, - IsClubPet: t.Pet.IsClubPet, + Images: t.Images, + IsSterile: *t.Pet.IsSterile, + IsVaccinated: *t.Pet.IsVaccinated, + IsVisible: *t.Pet.IsVaccinated, + IsClubPet: *t.Pet.IsClubPet, Background: t.Pet.Background, Address: t.Pet.Address, Contact: t.Pet.Contact, @@ -203,11 +189,11 @@ func (t *PetServiceTest) SetupTest() { Habit: t.Pet.Habit, Caption: t.Pet.Caption, Status: proto.PetStatus(t.Pet.Status), - ImageUrls: t.ImageUrls, - IsSterile: t.Pet.IsSterile, - IsVaccinated: t.Pet.IsVaccinated, - IsVisible: t.Pet.IsVisible, - IsClubPet: t.Pet.IsClubPet, + Images: t.Images, + IsSterile: *t.Pet.IsSterile, + IsVaccinated: *t.Pet.IsVaccinated, + IsVisible: *t.Pet.IsVisible, + IsClubPet: *t.Pet.IsClubPet, Background: t.Pet.Background, Address: t.Pet.Address, Contact: t.Pet.Contact, @@ -322,7 +308,7 @@ func (t *PetServiceTest) TestFindOneSuccess() { func (t *PetServiceTest) TestFindAllSuccess() { - want := &proto.FindAllPetResponse{Pets: t.createPetsDto(t.Pets, t.ImageUrlsList)} + want := &proto.FindAllPetResponse{Pets: t.createPetsDto(t.Pets, t.ImagesList)} var petsIn []*pet.Pet @@ -377,10 +363,10 @@ func createPets() []*pet.Pet { Habit: faker.Paragraph(), Caption: faker.Paragraph(), Status: petConst.Status(rand.Intn(1) + 1), - IsSterile: true, - IsVaccinated: true, - IsVisible: true, - IsClubPet: true, + IsSterile: utils.BoolAddr(true), + IsVaccinated: utils.BoolAddr(true), + IsVisible: utils.BoolAddr(true), + IsClubPet: utils.BoolAddr(true), Background: faker.Paragraph(), Address: faker.Paragraph(), Contact: faker.Paragraph(), @@ -391,7 +377,7 @@ func createPets() []*pet.Pet { return result } -func (t *PetServiceTest) createPetsDto(in []*pet.Pet, imageUrlsList [][]string) []*proto.Pet { +func (t *PetServiceTest) createPetsDto(in []*pet.Pet, images [][]*img_proto.Image) []*proto.Pet { var result []*proto.Pet for i, p := range in { @@ -405,11 +391,11 @@ func (t *PetServiceTest) createPetsDto(in []*pet.Pet, imageUrlsList [][]string) Habit: p.Habit, Caption: p.Caption, Status: proto.PetStatus(p.Status), - ImageUrls: imageUrlsList[i], - IsSterile: p.IsSterile, - IsVaccinated: p.IsVaccinated, - IsVisible: p.IsVisible, - IsClubPet: p.IsClubPet, + Images: images[i], + IsSterile: *p.IsSterile, + IsVaccinated: *p.IsVaccinated, + IsVisible: *p.IsVisible, + IsClubPet: *p.IsClubPet, Background: p.Background, Address: p.Address, Contact: p.Contact, @@ -423,7 +409,7 @@ func (t *PetServiceTest) createPetsDto(in []*pet.Pet, imageUrlsList [][]string) func (t *PetServiceTest) TestCreateSuccess() { want := &proto.CreatePetResponse{Pet: t.PetDto} - want.Pet.ImageUrls = []string{} // when pet is first created, it has no images + want.Pet.Images = nil // when pet is first created, it has no images repo := &mock.RepositoryMock{} diff --git a/src/app/utils/common.utils.go b/src/app/utils/common.utils.go new file mode 100644 index 0000000..f26e37b --- /dev/null +++ b/src/app/utils/common.utils.go @@ -0,0 +1,5 @@ +package utils + +func BoolAddr(b bool) *bool { + return &b +} diff --git a/src/mocks/image/image.mock.go b/src/mocks/image/image.mock.go index 19aba0d..7347f8f 100644 --- a/src/mocks/image/image.mock.go +++ b/src/mocks/image/image.mock.go @@ -12,6 +12,16 @@ type ClientMock struct { mock.Mock } +func (c *ClientMock) AssignPet(_ context.Context, in *proto.AssignPetRequest, opts ...grpc.CallOption) (res *proto.AssignPetResponse, err error) { + args := c.Called(in) + + if args.Get(0) != nil { + res = args.Get(0).(*proto.AssignPetResponse) + } + + return res, args.Error(1) +} + func (c *ClientMock) Upload(_ context.Context, in *proto.UploadImageRequest, _ ...grpc.CallOption) (res *proto.UploadImageResponse, err error) { args := c.Called(in) diff --git a/src/mocks/pet/pet.mock.go b/src/mocks/pet/pet.mock.go index d1f5d87..fd0004a 100644 --- a/src/mocks/pet/pet.mock.go +++ b/src/mocks/pet/pet.mock.go @@ -53,3 +53,13 @@ func (r *RepositoryMock) Delete(id string) error { args := r.Called(id) return args.Error(0) } + +func (r *RepositoryMock) ChangeView(id string, visible bool, result *bool) error { + args := r.Called(id, result) + + if args.Get(0) != nil { + *result = args.Get(0).(bool) + } + + return args.Error(1) +}