From 02561c049e777056c6b6da26c21e10626a723df2 Mon Sep 17 00:00:00 2001 From: KinTrae Date: Tue, 28 Jan 2025 23:06:34 +0100 Subject: [PATCH] feature: MATCHING: add endpoints for adding/deleting a single picture + add indexes task: 8697q6e93 --- .../MatchingProfileController.java | 22 +-- .../MatchingProfilePictureController.java | 54 ++++++ .../matching/dtos/MatchingProfileDto.java | 2 +- .../models/MatchingProfilePicture.java | 7 +- .../MatchingProfilePictureService.java | 36 ++++ .../services/MatchingProfileQueryService.java | 19 ++ .../services/MatchingProfileService.java | 20 --- .../MatchingProfileValidationService.java | 5 + .../MatchingProfilePictureServiceImpl.java | 162 ++++++++++++++++++ .../impl/MatchingProfileQueryServiceImpl.java | 45 +++++ .../impl/MatchingProfileServiceImpl.java | 128 ++------------ .../MatchingProfileValidationServiceImpl.java | 22 +++ .../backend/shared/dtos/PictureDto.java | 8 + database/scripts/120_create_tables.sql | 1 + 14 files changed, 377 insertions(+), 154 deletions(-) create mode 100644 backend/src/main/java/meowhub/backend/matching/controllers/MatchingProfilePictureController.java create mode 100644 backend/src/main/java/meowhub/backend/matching/services/MatchingProfilePictureService.java create mode 100644 backend/src/main/java/meowhub/backend/matching/services/MatchingProfileQueryService.java create mode 100644 backend/src/main/java/meowhub/backend/matching/services/MatchingProfileValidationService.java create mode 100644 backend/src/main/java/meowhub/backend/matching/services/impl/MatchingProfilePictureServiceImpl.java create mode 100644 backend/src/main/java/meowhub/backend/matching/services/impl/MatchingProfileQueryServiceImpl.java create mode 100644 backend/src/main/java/meowhub/backend/matching/services/impl/MatchingProfileValidationServiceImpl.java diff --git a/backend/src/main/java/meowhub/backend/matching/controllers/MatchingProfileController.java b/backend/src/main/java/meowhub/backend/matching/controllers/MatchingProfileController.java index 75de06be..c9e1b355 100644 --- a/backend/src/main/java/meowhub/backend/matching/controllers/MatchingProfileController.java +++ b/backend/src/main/java/meowhub/backend/matching/controllers/MatchingProfileController.java @@ -4,8 +4,8 @@ import meowhub.backend.matching.dtos.CreateMatchingProfileRequestDto; import meowhub.backend.matching.dtos.UpdateMatchingProfileRequestDto; import meowhub.backend.matching.dtos.MatchingProfileDto; +import meowhub.backend.matching.services.MatchingProfileQueryService; import meowhub.backend.matching.services.MatchingProfileService; -import meowhub.backend.shared.dtos.PictureDto; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; @@ -16,21 +16,18 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.multipart.MultipartFile; - -import java.util.List; @RestController @RequestMapping("/api/matching-profile") @AllArgsConstructor public class MatchingProfileController { private final MatchingProfileService matchingProfileService; + private final MatchingProfileQueryService matchingProfileQueryService; @GetMapping("") public ResponseEntity getMyMatchingProfile(@AuthenticationPrincipal UserDetails userDetails) { - return ResponseEntity.ok(matchingProfileService.getMyProfile(userDetails.getUsername())); + return ResponseEntity.ok(matchingProfileQueryService.getMyProfile(userDetails.getUsername())); } @PostMapping("/update") @@ -38,11 +35,6 @@ public ResponseEntity updateMatchingProfile(@RequestBody Upd return ResponseEntity.ok(matchingProfileService.updateMatchingProfile(matchingProfileDto, userDetails.getUsername())); } - @PostMapping("/pictures") - public ResponseEntity> addMatchingProfilePictures(@RequestPart List files, @RequestPart(required = false) String profilePictureName, @AuthenticationPrincipal UserDetails userDetails) { - return ResponseEntity.ok(matchingProfileService.addMatchingProfilePictures(files, profilePictureName, userDetails.getUsername())); - } - @PostMapping("/create") public ResponseEntity createMatchingProfileBasedOnAccount(@AuthenticationPrincipal UserDetails userDetails) { return ResponseEntity.ok(matchingProfileService.createMatchingProfileBasedOnAccount(userDetails.getUsername())); @@ -55,17 +47,11 @@ public ResponseEntity createMatchingProfileFromScratch(@Requ @GetMapping("/all") public ResponseEntity> getAllMatchingProfiles(Pageable pageable) { - return ResponseEntity.ok(matchingProfileService.getAllMatchingProfiles(pageable)); + return ResponseEntity.ok(matchingProfileQueryService.getAllMatchingProfiles(pageable)); } @DeleteMapping() public void deleteMatchingProfile(@AuthenticationPrincipal UserDetails userDetails) { matchingProfileService.deleteMatchingProfile(userDetails.getUsername()); } - - @DeleteMapping("/pictures") - public void deleteMatchingProfilePicture(@RequestBody List pictureIds, @AuthenticationPrincipal UserDetails userDetails) { - matchingProfileService.deleteMatchingProfilePicturesForUser(pictureIds, userDetails.getUsername()); - } - } diff --git a/backend/src/main/java/meowhub/backend/matching/controllers/MatchingProfilePictureController.java b/backend/src/main/java/meowhub/backend/matching/controllers/MatchingProfilePictureController.java new file mode 100644 index 00000000..8d37b083 --- /dev/null +++ b/backend/src/main/java/meowhub/backend/matching/controllers/MatchingProfilePictureController.java @@ -0,0 +1,54 @@ +package meowhub.backend.matching.controllers; + +import lombok.RequiredArgsConstructor; +import meowhub.backend.matching.services.MatchingProfilePictureService; +import meowhub.backend.shared.dtos.PictureDto; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/api/matching-profile-picture") +@RequiredArgsConstructor +public class MatchingProfilePictureController { + private final MatchingProfilePictureService matchingProfilePictureService; + + @PostMapping() + public ResponseEntity addPicture(@RequestPart MultipartFile file, @RequestPart String pictureIndex, @AuthenticationPrincipal UserDetails userDetails) { + return ResponseEntity.ok(matchingProfilePictureService.addMatchingProfilePicture(file, Long.valueOf(pictureIndex), userDetails.getUsername())); + } + + @PostMapping("/indexes") + public ResponseEntity> setIndexes(@RequestBody Map pictureIndexes, @AuthenticationPrincipal UserDetails userDetails) { + return ResponseEntity.ok(matchingProfilePictureService.setIndexes(pictureIndexes, userDetails.getUsername())); + } + + @DeleteMapping("/{pictureId}") + public void deletePicture(@PathVariable("pictureId") String pictureId, @AuthenticationPrincipal UserDetails userDetails) { + matchingProfilePictureService.deleteMatchingProfilePictureForUser(pictureId, userDetails.getUsername()); + } + + //-------------------------------------------------------------------------------- UNUSED -------------------------------------------------------------------------------- + @PostMapping("/pictures") + @Deprecated(forRemoval = true) + public ResponseEntity> addMatchingProfilePictures(@RequestPart List files, @RequestPart(required = false) String profilePictureName, @AuthenticationPrincipal UserDetails userDetails) { + return ResponseEntity.ok(matchingProfilePictureService.addMatchingProfilePictures(files, profilePictureName, userDetails.getUsername())); + } + + @DeleteMapping("/pictures") + @Deprecated(forRemoval = true) + public void deleteMatchingProfilePicture(@RequestBody List pictureIds, @AuthenticationPrincipal UserDetails userDetails) { + matchingProfilePictureService.deleteMatchingProfilePicturesForUser(pictureIds, userDetails.getUsername()); + } +} diff --git a/backend/src/main/java/meowhub/backend/matching/dtos/MatchingProfileDto.java b/backend/src/main/java/meowhub/backend/matching/dtos/MatchingProfileDto.java index 3316db02..1cee19a9 100644 --- a/backend/src/main/java/meowhub/backend/matching/dtos/MatchingProfileDto.java +++ b/backend/src/main/java/meowhub/backend/matching/dtos/MatchingProfileDto.java @@ -60,6 +60,6 @@ public static MatchingProfileDto createFromMatchingProfile(MatchingProfile match } private static PictureDto createFromPicture(MatchingProfilePicture picture) { - return new PictureDto(picture.getId(), picture.getOciUrl(), picture.getCreatedAt()); + return new PictureDto(picture.getId(), picture.getOciUrl(), picture.getIndex(), picture.getCreatedAt()); } } diff --git a/backend/src/main/java/meowhub/backend/matching/models/MatchingProfilePicture.java b/backend/src/main/java/meowhub/backend/matching/models/MatchingProfilePicture.java index c4b67cd6..b13618be 100644 --- a/backend/src/main/java/meowhub/backend/matching/models/MatchingProfilePicture.java +++ b/backend/src/main/java/meowhub/backend/matching/models/MatchingProfilePicture.java @@ -47,6 +47,10 @@ public class MatchingProfilePicture { @Column(name = "OCI_URL", nullable = false, length = 2000) private String ociUrl; + @NotNull + @Column(name = "PICTURE_INDEX", nullable = false) + private Long index; + @NotNull @Column(name = "IS_CURRENT_PP", nullable = false) @Convert(converter = BooleanConverter.class) @@ -66,10 +70,11 @@ public class MatchingProfilePicture { @Column(name = "MODIFIED_BY", length = 36) private String modifiedBy; - public MatchingProfilePicture(MatchingProfile matchingProfile, String ociName, String ociUrl, boolean isCurrentProfilePicture) { + public MatchingProfilePicture(MatchingProfile matchingProfile, String ociName, String ociUrl, boolean isCurrentProfilePicture, Long index) { this.matchingProfile = matchingProfile; this.ociName = ociName; this.ociUrl = ociUrl; this.isCurrentProfilePicture = isCurrentProfilePicture; + this.index = index; } } \ No newline at end of file diff --git a/backend/src/main/java/meowhub/backend/matching/services/MatchingProfilePictureService.java b/backend/src/main/java/meowhub/backend/matching/services/MatchingProfilePictureService.java new file mode 100644 index 00000000..681e4920 --- /dev/null +++ b/backend/src/main/java/meowhub/backend/matching/services/MatchingProfilePictureService.java @@ -0,0 +1,36 @@ +package meowhub.backend.matching.services; + +import meowhub.backend.shared.dtos.PictureDto; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; +import java.util.Map; + +public interface MatchingProfilePictureService { + PictureDto addMatchingProfilePicture(MultipartFile file, Long index, String login); + + List setIndexes(Map pictureIndexes, String login); + + void deleteMatchingProfilePictureForUser(String pictureId, String login); + + void intDeleteMatchingProfilePicture(String pictureId); + + /*** + * Adds pictures to the matching profile. + * Since currently frontend does not support adding multiple pictures in single request to the profile, this method is deprecated. + * @param files, max 5 pictures + * @param profilePictureName - name of the picture in files list, that is to be set as new profile picture + * @param login + * @return + * @deprecated + */ + @Deprecated + List addMatchingProfilePictures(List files, String profilePictureName, String login); + + /** + * Since currently frontend does not support deleting multiple pictures in single request, this method is deprecated. + * @deprecated + */ + @Deprecated + void deleteMatchingProfilePicturesForUser(List profilePictureIds, String login); +} diff --git a/backend/src/main/java/meowhub/backend/matching/services/MatchingProfileQueryService.java b/backend/src/main/java/meowhub/backend/matching/services/MatchingProfileQueryService.java new file mode 100644 index 00000000..4458adf5 --- /dev/null +++ b/backend/src/main/java/meowhub/backend/matching/services/MatchingProfileQueryService.java @@ -0,0 +1,19 @@ +package meowhub.backend.matching.services; + + +import meowhub.backend.matching.dtos.MatchingProfileDto; +import meowhub.backend.matching.models.MatchingProfile; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +import java.util.Optional; + +public interface MatchingProfileQueryService { + Page getAllMatchingProfiles(Pageable pageable); + + MatchingProfileDto getMyProfile(String login); + + MatchingProfile findMatchingProfileByLoginOrThrow(String login); + + Optional findMatchingProfileByLogin(String login); +} diff --git a/backend/src/main/java/meowhub/backend/matching/services/MatchingProfileService.java b/backend/src/main/java/meowhub/backend/matching/services/MatchingProfileService.java index 1e8c9560..efc63bbb 100644 --- a/backend/src/main/java/meowhub/backend/matching/services/MatchingProfileService.java +++ b/backend/src/main/java/meowhub/backend/matching/services/MatchingProfileService.java @@ -3,17 +3,8 @@ import meowhub.backend.matching.dtos.CreateMatchingProfileRequestDto; import meowhub.backend.matching.dtos.UpdateMatchingProfileRequestDto; import meowhub.backend.matching.dtos.MatchingProfileDto; -import meowhub.backend.shared.dtos.PictureDto; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.web.multipart.MultipartFile; - -import java.util.List; public interface MatchingProfileService { - Page getAllMatchingProfiles(Pageable pageable); - - MatchingProfileDto getMyProfile(String login); MatchingProfileDto createMatchingProfileBasedOnAccount(String login); @@ -21,16 +12,5 @@ public interface MatchingProfileService { MatchingProfileDto updateMatchingProfile(UpdateMatchingProfileRequestDto matchingProfileDto, String login); - /*** - * Adds pictures to the matching profile. - * @param files, max 5 pictures - * @param profilePictureName - name of the picture in files list, that is to be set as new profile picture - * @param login - * @return - */ - List addMatchingProfilePictures(List files, String profilePictureName, String login); - void deleteMatchingProfile(String login); - - void deleteMatchingProfilePicturesForUser(List profilePictureIds, String login); } diff --git a/backend/src/main/java/meowhub/backend/matching/services/MatchingProfileValidationService.java b/backend/src/main/java/meowhub/backend/matching/services/MatchingProfileValidationService.java new file mode 100644 index 00000000..a6ce664a --- /dev/null +++ b/backend/src/main/java/meowhub/backend/matching/services/MatchingProfileValidationService.java @@ -0,0 +1,5 @@ +package meowhub.backend.matching.services; + +public interface MatchingProfileValidationService { + void validateIfMatchingProfileAlreadyExists(String login); +} \ No newline at end of file diff --git a/backend/src/main/java/meowhub/backend/matching/services/impl/MatchingProfilePictureServiceImpl.java b/backend/src/main/java/meowhub/backend/matching/services/impl/MatchingProfilePictureServiceImpl.java new file mode 100644 index 00000000..138d4dd9 --- /dev/null +++ b/backend/src/main/java/meowhub/backend/matching/services/impl/MatchingProfilePictureServiceImpl.java @@ -0,0 +1,162 @@ +package meowhub.backend.matching.services.impl; + +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; +import meowhub.backend.matching.models.MatchingProfile; +import meowhub.backend.matching.models.MatchingProfilePicture; +import meowhub.backend.matching.repositories.MatchingProfilePictureRepository; +import meowhub.backend.matching.services.MatchingProfilePictureService; +import meowhub.backend.matching.services.MatchingProfileQueryService; +import meowhub.backend.shared.constants.AlertConstants; +import meowhub.backend.shared.constants.Modules; +import meowhub.backend.shared.dtos.PictureDto; +import meowhub.backend.shared.utils.PictureUtils; +import org.springframework.data.util.Pair; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +@Service +@RequiredArgsConstructor +@Transactional +public class MatchingProfilePictureServiceImpl implements MatchingProfilePictureService { + private static final String MATCHING_PROFILE_PICTURE = "Matching profile picture"; + + private final MatchingProfilePictureRepository matchingProfilePictureRepository; + private final MatchingProfileQueryService matchingProfileQueryService; + private final PictureUtils pictureUtils; + + @Override + @Transactional + public PictureDto addMatchingProfilePicture(MultipartFile file, Long index, String login) { + MatchingProfile matchingProfile = matchingProfileQueryService.findMatchingProfileByLoginOrThrow(login); + boolean isCurrentProfilePicture = index == 0; + + //find current profile picture and set it to false if exists or if it does not and added picture is not a current profile picture - throw exception + int currentlyPictures = matchingProfilePictureRepository.countAllByMatchingProfileId(matchingProfile.getId()); + Optional currentProfilePicture = matchingProfilePictureRepository.findByMatchingProfileUserLoginAndIsCurrentProfilePictureTrue(login); + if (currentProfilePicture.isPresent()) { + MatchingProfilePicture current = currentProfilePicture.get(); + current.setIsCurrentProfilePicture(false); + matchingProfilePictureRepository.save(current); + + } else { + if (!isCurrentProfilePicture && currentlyPictures == 0) { + throw new NullPointerException(String.format(AlertConstants.VALUE_REQUIRED, "profilePictureName, when there is no profile picture")); + } + } + + //we allow to have max 5 profile pictures + if (currentlyPictures >= 5) { + throw new IllegalArgumentException(String.format(AlertConstants.TOO_MANY_PICTURES, 5, currentlyPictures + 1)); + } + + //upload picture to OCI and save it to db + Pair pictureInfo = pictureUtils.uploadPictureToOCIAndGetAuthorizedUrlToAccessIt(file, login, Modules.MATCHING_PROFILE); + MatchingProfilePicture matchingProfilePicture = new MatchingProfilePicture(matchingProfile, pictureInfo.getFirst(), pictureInfo.getSecond(), isCurrentProfilePicture, index); + matchingProfilePicture.setCreatedAt(LocalDateTime.now()); + matchingProfilePicture = matchingProfilePictureRepository.save(matchingProfilePicture); + + return matchingProfilePictureRepository.findById(matchingProfilePicture.getId()) + .map(picture -> new PictureDto(picture.getId(), picture.getOciUrl(), picture.getIndex(), picture.getCreatedAt())) + .orElseThrow(); + } + + @Override + @Transactional + public List setIndexes(Map pictureIndexes, String login) { + List pictureIds = new ArrayList<>(pictureIndexes.keySet()); + pictureIds.forEach(pictureId -> validateIfExists(pictureId, login)); + + pictureIds.forEach(pictureId -> { + MatchingProfilePicture picture = matchingProfilePictureRepository.findById(pictureId).orElseThrow(); + picture.setIndex(pictureIndexes.get(pictureId)); + matchingProfilePictureRepository.save(picture); + }); + + return matchingProfilePictureRepository.findByMatchingProfileUserLogin(login).stream() + .map(picture -> new PictureDto(picture.getId(), picture.getOciUrl(), picture.getIndex(), picture.getCreatedAt())) + .toList(); + } + + @Override + public void deleteMatchingProfilePictureForUser(String pictureId, String login) { + validateIfExists(pictureId, login); + intDeleteMatchingProfilePicture(pictureId); + } + + @Override + public void intDeleteMatchingProfilePicture(String pictureId) { + MatchingProfilePicture picture = matchingProfilePictureRepository.findById(pictureId).orElseThrow(); + pictureUtils.deletePictureFromOCI(picture.getOciName()); //delete from OCI + matchingProfilePictureRepository.deleteById(pictureId); //delete from db + } + + @Override + public List addMatchingProfilePictures(List pictures, String profilePictureName, String login) { + if (pictures == null || pictures.isEmpty() || pictures.getFirst().getContentType() == null) { + throw new IllegalArgumentException(AlertConstants.VALUE_REQUIRED_TITLE); + } + + MatchingProfile matchingProfile = matchingProfileQueryService.findMatchingProfileByLoginOrThrow(login); + int currentlyPictures = matchingProfilePictureRepository.countAllByMatchingProfileId(matchingProfile.getId()); + + if (profilePictureName != null && !profilePictureName.isEmpty()) { + //checking if given profile picture name (that is to be set as a new profile picture) exists in the list of pictures + pictures.stream().filter(picture -> Objects.equals(picture.getOriginalFilename(), profilePictureName)).findFirst() + .orElseThrow(() -> new IllegalArgumentException(String.format(AlertConstants.RESOURCE_NOT_FOUND, MATCHING_PROFILE_PICTURE, "name", profilePictureName))); + + //find current profile picture and set it to false if exists + Optional currentProfilePicture = matchingProfilePictureRepository.findByMatchingProfileUserLoginAndIsCurrentProfilePictureTrue(login); + if (currentProfilePicture.isPresent()) { + MatchingProfilePicture current = currentProfilePicture.get(); + current.setIsCurrentProfilePicture(false); + matchingProfilePictureRepository.save(current); + } + } else { + if (currentlyPictures == 0) { + throw new NullPointerException(String.format(AlertConstants.VALUE_REQUIRED, "profilePictureName, when there is no profile picture")); + } + } + + if (currentlyPictures + pictures.size() > 5) { + throw new IllegalArgumentException(String.format(AlertConstants.TOO_MANY_PICTURES, 5, currentlyPictures + pictures.size())); + } + + List profilePictures = new ArrayList<>(); + for (MultipartFile picture : pictures) { + boolean isCurrentProfilePicture = profilePictureName != null && !profilePictureName.isEmpty() && Objects.equals(picture.getOriginalFilename(), profilePictureName); + Pair pictureInfo = pictureUtils.uploadPictureToOCIAndGetAuthorizedUrlToAccessIt(picture, login, Modules.MATCHING_PROFILE); + profilePictures.add(new MatchingProfilePicture(matchingProfile, pictureInfo.getFirst(), pictureInfo.getSecond(), isCurrentProfilePicture, 0L)); + } + matchingProfilePictureRepository.saveAll(profilePictures); + + return matchingProfilePictureRepository.findByMatchingProfileUserLogin(login).stream() + .map(picture -> new PictureDto(picture.getId(), picture.getOciUrl(), picture.getCreatedAt())) + .toList(); + } + + @Override + public void deleteMatchingProfilePicturesForUser(List profilePictureIds, String login) { + if (profilePictureIds.isEmpty()) { + throw new IllegalArgumentException(AlertConstants.VALUE_REQUIRED_TITLE); + } + + //validate ids + profilePictureIds.forEach(pictureId -> validateIfExists(pictureId, login)); + + profilePictureIds.forEach(this::intDeleteMatchingProfilePicture); + } + + private void validateIfExists(String pictureId, String login) { + if (!matchingProfilePictureRepository.existsByIdAndMatchingProfileUserLogin(pictureId, login)) { + throw new IllegalArgumentException(String.format(AlertConstants.RESOURCE_NOT_FOUND, MATCHING_PROFILE_PICTURE, "id", pictureId)); + } + } +} diff --git a/backend/src/main/java/meowhub/backend/matching/services/impl/MatchingProfileQueryServiceImpl.java b/backend/src/main/java/meowhub/backend/matching/services/impl/MatchingProfileQueryServiceImpl.java new file mode 100644 index 00000000..619872b2 --- /dev/null +++ b/backend/src/main/java/meowhub/backend/matching/services/impl/MatchingProfileQueryServiceImpl.java @@ -0,0 +1,45 @@ +package meowhub.backend.matching.services.impl; + +import lombok.RequiredArgsConstructor; +import meowhub.backend.matching.dtos.MatchingProfileDto; +import meowhub.backend.matching.models.MatchingProfile; +import meowhub.backend.matching.repositories.MatchingProfileRepository; +import meowhub.backend.matching.services.MatchingProfileQueryService; +import meowhub.backend.shared.constants.AlertConstants; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +import java.util.Optional; + +import static meowhub.backend.shared.constants.Modules.MATCHING_PROFILE; + +@Service +@RequiredArgsConstructor +public class MatchingProfileQueryServiceImpl implements MatchingProfileQueryService { + private final MatchingProfileRepository matchingProfileRepository; + + @Override + public Page getAllMatchingProfiles(Pageable pageable) { + return matchingProfileRepository.findAll(pageable).map(MatchingProfileDto::createFromMatchingProfile); + } + + @Override + public MatchingProfileDto getMyProfile(String login) { + if (findMatchingProfileByLogin(login).isEmpty()) { + return null; + } + return matchingProfileRepository.findByUserLogin(login).map(MatchingProfileDto::createFromMatchingProfile).orElseThrow(); + } + + @Override + public MatchingProfile findMatchingProfileByLoginOrThrow(String login) { + return matchingProfileRepository.findByUserLogin(login) + .orElseThrow(() -> new IllegalArgumentException(String.format(AlertConstants.RESOURCE_NOT_FOUND, MATCHING_PROFILE, "login", login))); + } + + @Override + public Optional findMatchingProfileByLogin(String login) { + return matchingProfileRepository.findByUserLogin(login); + } +} diff --git a/backend/src/main/java/meowhub/backend/matching/services/impl/MatchingProfileServiceImpl.java b/backend/src/main/java/meowhub/backend/matching/services/impl/MatchingProfileServiceImpl.java index aecb66a1..6ee123fd 100644 --- a/backend/src/main/java/meowhub/backend/matching/services/impl/MatchingProfileServiceImpl.java +++ b/backend/src/main/java/meowhub/backend/matching/services/impl/MatchingProfileServiceImpl.java @@ -6,64 +6,49 @@ import meowhub.backend.matching.dtos.UpdateMatchingProfileRequestDto; import meowhub.backend.matching.dtos.MatchingProfileDto; import meowhub.backend.matching.models.MatchingProfile; -import meowhub.backend.matching.models.MatchingProfilePicture; import meowhub.backend.matching.repositories.EducationRepository; import meowhub.backend.matching.repositories.HowOftenRepository; import meowhub.backend.matching.repositories.LookingForRepository; -import meowhub.backend.matching.repositories.MatchingProfilePictureRepository; import meowhub.backend.matching.repositories.MatchingProfileRepository; import meowhub.backend.matching.repositories.PetRepository; import meowhub.backend.matching.repositories.SexualityRepository; +import meowhub.backend.matching.services.MatchingProfilePictureService; +import meowhub.backend.matching.services.MatchingProfileQueryService; import meowhub.backend.matching.services.MatchingProfileService; +import meowhub.backend.matching.services.MatchingProfileValidationService; import meowhub.backend.shared.constants.AlertConstants; -import meowhub.backend.shared.constants.Modules; -import meowhub.backend.shared.dtos.PictureDto; -import meowhub.backend.shared.utils.PictureUtils; import meowhub.backend.users.facades.UserMatchingServiceFacade; import meowhub.backend.users.models.Gender; import meowhub.backend.users.models.User; import meowhub.backend.users.repositories.GenderRepository; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.util.Pair; import org.springframework.stereotype.Service; -import org.springframework.web.multipart.MultipartFile; import java.time.LocalDate; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; @Service @RequiredArgsConstructor public class MatchingProfileServiceImpl implements MatchingProfileService { private static final int MINIMUM_AGE = 16; - private static final String MATCHING_PROFILE = "Matching profile"; private static final String HOW_OFTEN = "HowOften"; - private final MatchingProfileRepository matchingProfileRepository; - private final MatchingProfilePictureRepository matchingProfilePictureRepository; + private final MatchingProfilePictureService matchingProfilePictureService; + private final MatchingProfileValidationService matchingProfileValidationService; + private final MatchingProfileQueryService matchingProfileQueryService; private final UserMatchingServiceFacade userMatchingServiceFacade; + + private final MatchingProfileRepository matchingProfileRepository; private final PetRepository petRepository; private final SexualityRepository sexualityRepository; private final LookingForRepository lookingForRepository; private final HowOftenRepository howOftenRepository; private final EducationRepository educationRepository; - private final PictureUtils pictureUtils; private final GenderRepository genderRepository; - @Override - public MatchingProfileDto getMyProfile(String login) { - if (!matchingProfileRepository.existsByUserLogin(login)) { - return null; - } - return matchingProfileRepository.findByUserLogin(login).map(MatchingProfileDto::createFromMatchingProfile).orElseThrow(); - } + @Override public MatchingProfileDto createMatchingProfileBasedOnAccount(String login) { - validateIfMatchingProfileAlreadyExists(login); + matchingProfileValidationService.validateIfMatchingProfileAlreadyExists(login); User user = userMatchingServiceFacade.findUserByLogin(login); MatchingProfile matchingProfile = new MatchingProfile(); @@ -78,7 +63,7 @@ public MatchingProfileDto createMatchingProfileBasedOnAccount(String login) { @Override public MatchingProfileDto createMatchingProfileFromScratch(CreateMatchingProfileRequestDto request, String login) { - validateIfMatchingProfileAlreadyExists(login); + matchingProfileValidationService.validateIfMatchingProfileAlreadyExists(login); if (request.getBirthdate() == null || request.getBirthdate().isAfter(LocalDate.now().minusYears(MINIMUM_AGE))) { throw new IllegalArgumentException(String.format(AlertConstants.ILLEGAL_ARGUMENT, "birthdate", " null and must be at least " + MINIMUM_AGE + " years old")); @@ -102,63 +87,12 @@ public MatchingProfileDto createMatchingProfileFromScratch(CreateMatchingProfile @Override public MatchingProfileDto updateMatchingProfile(UpdateMatchingProfileRequestDto matchingProfileDto, String login) { - MatchingProfile matchingProfile = findMatchingProfileByLoginOrThrow(login); + MatchingProfile matchingProfile = matchingProfileQueryService.findMatchingProfileByLoginOrThrow(login); updateMatchingProfile(matchingProfile, matchingProfileDto); matchingProfileRepository.save(matchingProfile); return MatchingProfileDto.createFromMatchingProfile(matchingProfile); } - @Override - @Transactional - public List addMatchingProfilePictures(List pictures, String profilePictureName, String login) { - if (pictures == null || pictures.isEmpty() || pictures.getFirst().getContentType() == null) { - throw new IllegalArgumentException(AlertConstants.VALUE_REQUIRED_TITLE); - } - - MatchingProfile matchingProfile = findMatchingProfileByLoginOrThrow(login); - int currentlyPictures = matchingProfilePictureRepository.countAllByMatchingProfileId(matchingProfile.getId()); - - if (profilePictureName != null && !profilePictureName.isEmpty()) { - //checking if given profile picture name (that is to be set as a new profile picture) exists in the list of pictures - pictures.stream().filter(picture -> Objects.equals(picture.getOriginalFilename(), profilePictureName)).findFirst() - .orElseThrow(() -> new IllegalArgumentException(String.format(AlertConstants.RESOURCE_NOT_FOUND, "Matching profile picture", "name", profilePictureName))); - - //find current profile picture and set it to false if exists - Optional currentProfilePicture = matchingProfilePictureRepository.findByMatchingProfileUserLoginAndIsCurrentProfilePictureTrue(login); - if (currentProfilePicture.isPresent()) { - MatchingProfilePicture current = currentProfilePicture.get(); - current.setIsCurrentProfilePicture(false); - matchingProfilePictureRepository.save(current); - } - } else { - if (currentlyPictures == 0) { - throw new NullPointerException(String.format(AlertConstants.VALUE_REQUIRED, "profilePictureName, when there is no profile picture")); - } - } - - - if (currentlyPictures + pictures.size() > 5) { - throw new IllegalArgumentException(String.format(AlertConstants.TOO_MANY_PICTURES, 5, currentlyPictures + pictures.size())); - } - - List profilePictures = new ArrayList<>(); - for (MultipartFile picture : pictures) { - boolean isCurrentProfilePicture = profilePictureName != null && !profilePictureName.isEmpty() && Objects.equals(picture.getOriginalFilename(), profilePictureName); - Pair pictureInfo = pictureUtils.uploadPictureToOCIAndGetAuthorizedUrlToAccessIt(picture, login, Modules.MATCHING_PROFILE); - profilePictures.add(new MatchingProfilePicture(matchingProfile, pictureInfo.getFirst(), pictureInfo.getSecond(), isCurrentProfilePicture)); - } - matchingProfilePictureRepository.saveAll(profilePictures); - - return matchingProfilePictureRepository.findByMatchingProfileUserLogin(login).stream() - .map(picture -> new PictureDto(picture.getId(), picture.getOciUrl(), picture.getCreatedAt())) - .toList(); - } - - @Override - public Page getAllMatchingProfiles(Pageable pageable) { - return matchingProfileRepository.findAll(pageable).map(MatchingProfileDto::createFromMatchingProfile); - } - private void updateMatchingProfile(MatchingProfile matchingProfile, UpdateMatchingProfileRequestDto request) { if (request.getPet() != null) { matchingProfile.setPets(petRepository.findByCode(request.getPet().name()) @@ -222,43 +156,9 @@ private void updateMatchingProfile(MatchingProfile matchingProfile, UpdateMatchi @Override @Transactional public void deleteMatchingProfile(String login) { - MatchingProfile matchingProfile = matchingProfileRepository.findByUserLogin(login) - .orElseThrow(() -> new IllegalArgumentException(String.format(AlertConstants.RESOURCE_NOT_FOUND, MATCHING_PROFILE, "login", login))); + MatchingProfile matchingProfile = matchingProfileQueryService.findMatchingProfileByLoginOrThrow(login); - matchingProfile.getMatchingProfilePictures().forEach(picture -> deleteMatchingProfilePicture(picture.getId())); + matchingProfile.getMatchingProfilePictures().forEach(picture -> matchingProfilePictureService.intDeleteMatchingProfilePicture(picture.getId())); matchingProfileRepository.delete(matchingProfile); } - - @Override - @Transactional - public void deleteMatchingProfilePicturesForUser(List profilePictureIds, String login) { - if (profilePictureIds.isEmpty()) { - throw new IllegalArgumentException(AlertConstants.VALUE_REQUIRED_TITLE); - } - - profilePictureIds.forEach(pictureId -> { - if (!matchingProfilePictureRepository.existsByIdAndMatchingProfileUserLogin(pictureId, login)) { - throw new IllegalArgumentException(String.format(AlertConstants.RESOURCE_NOT_FOUND, "Matching profile picture", "id", pictureId)); - } - }); - - profilePictureIds.forEach(this::deleteMatchingProfilePicture); - } - - private void deleteMatchingProfilePicture(String pictureId) { - MatchingProfilePicture picture = matchingProfilePictureRepository.findById(pictureId).orElseThrow(); - pictureUtils.deletePictureFromOCI(picture.getOciName()); //delete from OCI - matchingProfilePictureRepository.deleteById(pictureId); //delete from db - } - - private MatchingProfile findMatchingProfileByLoginOrThrow(String login) { - return matchingProfileRepository.findByUserLogin(login) - .orElseThrow(() -> new IllegalArgumentException(String.format(AlertConstants.RESOURCE_NOT_FOUND, MATCHING_PROFILE, "login", login))); - } - - private void validateIfMatchingProfileAlreadyExists(String login) { - if (matchingProfileRepository.existsByUserLogin(login)) { - throw new IllegalArgumentException(String.format(AlertConstants.ALREADY_EXISTS, MATCHING_PROFILE, login)); - } - } } diff --git a/backend/src/main/java/meowhub/backend/matching/services/impl/MatchingProfileValidationServiceImpl.java b/backend/src/main/java/meowhub/backend/matching/services/impl/MatchingProfileValidationServiceImpl.java new file mode 100644 index 00000000..7f39e126 --- /dev/null +++ b/backend/src/main/java/meowhub/backend/matching/services/impl/MatchingProfileValidationServiceImpl.java @@ -0,0 +1,22 @@ +package meowhub.backend.matching.services.impl; + +import lombok.RequiredArgsConstructor; +import meowhub.backend.matching.repositories.MatchingProfileRepository; +import meowhub.backend.matching.services.MatchingProfileValidationService; +import meowhub.backend.shared.constants.AlertConstants; +import org.springframework.stereotype.Service; + +import static meowhub.backend.shared.constants.Modules.MATCHING_PROFILE; + +@Service +@RequiredArgsConstructor +public class MatchingProfileValidationServiceImpl implements MatchingProfileValidationService { + private final MatchingProfileRepository matchingProfileRepository; + + @Override + public void validateIfMatchingProfileAlreadyExists(String login) { + if (matchingProfileRepository.existsByUserLogin(login)) { + throw new IllegalArgumentException(String.format(AlertConstants.ALREADY_EXISTS, MATCHING_PROFILE, login)); + } + } +} diff --git a/backend/src/main/java/meowhub/backend/shared/dtos/PictureDto.java b/backend/src/main/java/meowhub/backend/shared/dtos/PictureDto.java index fd9b0992..fe2dce0a 100644 --- a/backend/src/main/java/meowhub/backend/shared/dtos/PictureDto.java +++ b/backend/src/main/java/meowhub/backend/shared/dtos/PictureDto.java @@ -14,5 +14,13 @@ public class PictureDto { private String id; private String url; + private Long index; private LocalDateTime createdAt; + + //for any other than matching profile picture + public PictureDto(String id, String url, LocalDateTime createdAt) { + this.id = id; + this.url = url; + this.createdAt = createdAt; + } } diff --git a/database/scripts/120_create_tables.sql b/database/scripts/120_create_tables.sql index 0b4c8e5a..ff04dc82 100644 --- a/database/scripts/120_create_tables.sql +++ b/database/scripts/120_create_tables.sql @@ -325,6 +325,7 @@ CREATE TABLE mh_matching.Matching_Profile_Pictures matching_profile_id varchar2(36) NOT NULL, oci_name varchar2(100) NOT NULL, oci_url varchar2(2000) NOT NULL, + picture_index number(1) NOT NULL, is_current_pp number(1) NOT NULL, created_at date NOT NULL, created_by varchar2(36) NOT NULL,