From aebbb40ed4990988697674046d8284929493c11f Mon Sep 17 00:00:00 2001 From: Deniz Unal Date: Wed, 22 Nov 2023 21:33:56 +0300 Subject: [PATCH 1/7] created achievement entity --- .../app/gamereview/enums/AchievementType.java | 6 +++++ .../com/app/gamereview/model/Achievement.java | 27 +++++++++++++++++++ .../repository/AchievementRepository.java | 13 +++++++++ .../annotation/ValidAchievementType.java | 22 +++++++++++++++ .../validator/AchievementTypeValidator.java | 24 +++++++++++++++++ 5 files changed, 92 insertions(+) create mode 100644 app/backend/src/main/java/com/app/gamereview/enums/AchievementType.java create mode 100644 app/backend/src/main/java/com/app/gamereview/model/Achievement.java create mode 100644 app/backend/src/main/java/com/app/gamereview/repository/AchievementRepository.java create mode 100644 app/backend/src/main/java/com/app/gamereview/util/validation/annotation/ValidAchievementType.java create mode 100644 app/backend/src/main/java/com/app/gamereview/util/validation/validator/AchievementTypeValidator.java diff --git a/app/backend/src/main/java/com/app/gamereview/enums/AchievementType.java b/app/backend/src/main/java/com/app/gamereview/enums/AchievementType.java new file mode 100644 index 00000000..9bcb90fb --- /dev/null +++ b/app/backend/src/main/java/com/app/gamereview/enums/AchievementType.java @@ -0,0 +1,6 @@ +package com.app.gamereview.enums; + +public enum AchievementType { + GAME, + META +} diff --git a/app/backend/src/main/java/com/app/gamereview/model/Achievement.java b/app/backend/src/main/java/com/app/gamereview/model/Achievement.java new file mode 100644 index 00000000..919de8c7 --- /dev/null +++ b/app/backend/src/main/java/com/app/gamereview/model/Achievement.java @@ -0,0 +1,27 @@ +package com.app.gamereview.model; + +import com.app.gamereview.enums.AchievementType; +import com.app.gamereview.model.common.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.data.mongodb.core.mapping.Document; + +@Document(collection = "Achievement") +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class Achievement extends BaseModel { + + private String title; + + private String description; + + private String icon; + + private AchievementType type; + + private String gameId; +} diff --git a/app/backend/src/main/java/com/app/gamereview/repository/AchievementRepository.java b/app/backend/src/main/java/com/app/gamereview/repository/AchievementRepository.java new file mode 100644 index 00000000..1f9e19ef --- /dev/null +++ b/app/backend/src/main/java/com/app/gamereview/repository/AchievementRepository.java @@ -0,0 +1,13 @@ +package com.app.gamereview.repository; + +import com.app.gamereview.model.Achievement; +import org.springframework.data.mongodb.repository.MongoRepository; + +import java.util.Optional; + +public interface AchievementRepository extends MongoRepository { + + Optional findByTitleAndIsDeletedFalse(String title); + + Optional findByIdAndIsDeletedFalse(String id); +} diff --git a/app/backend/src/main/java/com/app/gamereview/util/validation/annotation/ValidAchievementType.java b/app/backend/src/main/java/com/app/gamereview/util/validation/annotation/ValidAchievementType.java new file mode 100644 index 00000000..b2ef4e26 --- /dev/null +++ b/app/backend/src/main/java/com/app/gamereview/util/validation/annotation/ValidAchievementType.java @@ -0,0 +1,22 @@ +package com.app.gamereview.util.validation.annotation; + +import com.app.gamereview.enums.AchievementType; +import com.app.gamereview.util.validation.validator.AchievementTypeValidator; +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.FIELD, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +@Constraint(validatedBy = AchievementTypeValidator.class) +public @interface ValidAchievementType { + + AchievementType[] allowedValues(); + String message() default "Invalid AchievementType, allowed values are: {allowedValues}"; + Class[] groups() default {}; + Class[] payload() default {}; +} diff --git a/app/backend/src/main/java/com/app/gamereview/util/validation/validator/AchievementTypeValidator.java b/app/backend/src/main/java/com/app/gamereview/util/validation/validator/AchievementTypeValidator.java new file mode 100644 index 00000000..1b4ac2c2 --- /dev/null +++ b/app/backend/src/main/java/com/app/gamereview/util/validation/validator/AchievementTypeValidator.java @@ -0,0 +1,24 @@ +package com.app.gamereview.util.validation.validator; + +import com.app.gamereview.enums.AchievementType; +import com.app.gamereview.util.validation.annotation.ValidAchievementType; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +public class AchievementTypeValidator implements ConstraintValidator { + + @Override + public boolean isValid(String providedType, ConstraintValidatorContext context) { + if (providedType == null || providedType.isEmpty()) { + return false; + } + + for (AchievementType achievementType : AchievementType.values()) { + if (achievementType.name().equals(providedType)) { + return true; + } + } + + return false; + } +} From 516233c5412a2b43410fcd3f6bcd166d2f63dfdd Mon Sep 17 00:00:00 2001 From: Deniz Unal Date: Wed, 22 Nov 2023 22:07:37 +0300 Subject: [PATCH 2/7] added achievement crud endpoints --- .../controller/AchievementController.java | 65 +++++++++++ .../CreateAchievementRequestDto.java | 32 ++++++ .../UpdateAchievementRequestDto.java | 21 ++++ .../service/AchievementService.java | 102 ++++++++++++++++++ 4 files changed, 220 insertions(+) create mode 100644 app/backend/src/main/java/com/app/gamereview/controller/AchievementController.java create mode 100644 app/backend/src/main/java/com/app/gamereview/dto/request/achievement/CreateAchievementRequestDto.java create mode 100644 app/backend/src/main/java/com/app/gamereview/dto/request/achievement/UpdateAchievementRequestDto.java create mode 100644 app/backend/src/main/java/com/app/gamereview/service/AchievementService.java diff --git a/app/backend/src/main/java/com/app/gamereview/controller/AchievementController.java b/app/backend/src/main/java/com/app/gamereview/controller/AchievementController.java new file mode 100644 index 00000000..57386e6d --- /dev/null +++ b/app/backend/src/main/java/com/app/gamereview/controller/AchievementController.java @@ -0,0 +1,65 @@ +package com.app.gamereview.controller; + +import com.app.gamereview.dto.request.achievement.CreateAchievementRequestDto; +import com.app.gamereview.dto.request.achievement.UpdateAchievementRequestDto; +import com.app.gamereview.service.AchievementService; +import com.app.gamereview.util.validation.annotation.AdminRequired; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import com.app.gamereview.model.Achievement; + +import java.util.ArrayList; +import java.util.List; + +@RestController +@RequestMapping("/api/achievement") +@Validated +public class AchievementController { + + private final AchievementService achievementService; + + public AchievementController(AchievementService achievementService) { + this.achievementService = achievementService; + } + + @PostMapping("/create") + @AdminRequired + public ResponseEntity createAchievement(@Valid @RequestBody CreateAchievementRequestDto achievementDto, + @RequestHeader String Authorization, HttpServletRequest request) { + Achievement achievement = achievementService.createAchievement(achievementDto); + return ResponseEntity.ok(achievement); + } + + @PostMapping("/update") + @AdminRequired + public ResponseEntity updateAchievement(@RequestParam String id, + @Valid @RequestBody UpdateAchievementRequestDto updateAchievementRequestDto, + @RequestHeader String Authorization, HttpServletRequest request) { + Achievement achievement = achievementService.updateAchievement(id, updateAchievementRequestDto); + return ResponseEntity.ok(achievement); + } + + @DeleteMapping("/delete") + @AdminRequired + public ResponseEntity deleteAchievement(@RequestParam String id, @RequestHeader String Authorization, + HttpServletRequest request) { + Achievement achievement = achievementService.deleteAchievement(id); + return ResponseEntity.ok(achievement); + } + + //TODO + @GetMapping("/get-game-achievements") + public ResponseEntity> getGameAchievements() { + return ResponseEntity.ok(new ArrayList()); + } + + //TODO + @PostMapping("/grant-achievement") + public ResponseEntity grantAchievement() { + return ResponseEntity.ok(new Achievement()); + } +} diff --git a/app/backend/src/main/java/com/app/gamereview/dto/request/achievement/CreateAchievementRequestDto.java b/app/backend/src/main/java/com/app/gamereview/dto/request/achievement/CreateAchievementRequestDto.java new file mode 100644 index 00000000..8d04bd55 --- /dev/null +++ b/app/backend/src/main/java/com/app/gamereview/dto/request/achievement/CreateAchievementRequestDto.java @@ -0,0 +1,32 @@ +package com.app.gamereview.dto.request.achievement; + +import com.app.gamereview.enums.AchievementType; +import com.app.gamereview.util.validation.annotation.ValidAchievementType; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Pattern; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CreateAchievementRequestDto { + + @NotEmpty(message = "Achievement title cannot be empty.") + private String title; + + @NotEmpty(message = "Achievement description cannot be empty.") + private String description; + + @NotEmpty(message = "Achievement icon cannot be empty.") + private String icon; + + @ValidAchievementType(allowedValues = {AchievementType.GAME, AchievementType.META}) + @NotEmpty(message = "Achievement type cannot be empty.") + private AchievementType type; + + @Pattern(regexp = "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$", + message = "Game has invalid Id (UUID) format") + private String gameId; +} diff --git a/app/backend/src/main/java/com/app/gamereview/dto/request/achievement/UpdateAchievementRequestDto.java b/app/backend/src/main/java/com/app/gamereview/dto/request/achievement/UpdateAchievementRequestDto.java new file mode 100644 index 00000000..92805045 --- /dev/null +++ b/app/backend/src/main/java/com/app/gamereview/dto/request/achievement/UpdateAchievementRequestDto.java @@ -0,0 +1,21 @@ +package com.app.gamereview.dto.request.achievement; + +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class UpdateAchievementRequestDto { + + @NotBlank(message = "Game title cannot be blank.") + private String title; + + @NotBlank(message = "Game description cannot be blank.") + private String description; + + @NotBlank(message = "Game icon cannot be blank.") + private String icon; +} diff --git a/app/backend/src/main/java/com/app/gamereview/service/AchievementService.java b/app/backend/src/main/java/com/app/gamereview/service/AchievementService.java new file mode 100644 index 00000000..13a85274 --- /dev/null +++ b/app/backend/src/main/java/com/app/gamereview/service/AchievementService.java @@ -0,0 +1,102 @@ +package com.app.gamereview.service; + +import com.app.gamereview.dto.request.achievement.CreateAchievementRequestDto; +import com.app.gamereview.dto.request.achievement.UpdateAchievementRequestDto; +import com.app.gamereview.enums.AchievementType; +import com.app.gamereview.exception.BadRequestException; +import com.app.gamereview.exception.ResourceNotFoundException; +import com.app.gamereview.model.Achievement; +import com.app.gamereview.model.Game; +import com.app.gamereview.repository.AchievementRepository; +import com.app.gamereview.repository.GameRepository; +import org.modelmapper.ModelMapper; +import org.springframework.stereotype.Service; + +import java.util.Objects; +import java.util.Optional; + +@Service +public class AchievementService { + + private final AchievementRepository achievementRepository; + + private final GameRepository gameRepository; + + private final ModelMapper modelMapper; + + public AchievementService(AchievementRepository achievementRepository, GameRepository gameRepository, ModelMapper modelMapper) { + this.achievementRepository = achievementRepository; + this.gameRepository = gameRepository; + this.modelMapper = modelMapper; + } + + public Achievement createAchievement(CreateAchievementRequestDto achievementRequestDto) { + + Optional achievementOptional = achievementRepository.findByTitleAndIsDeletedFalse(achievementRequestDto.getTitle()); + + if (achievementOptional.isPresent() && Objects.equals(achievementOptional.get().getGameId(), achievementRequestDto.getGameId())) { + throw new BadRequestException("There is already an achievement with the same name."); + } + + if (achievementRequestDto.getType() == AchievementType.GAME) { + + String gameId = achievementRequestDto.getGameId(); + + if (gameId.isEmpty()) { + throw new BadRequestException("Game id cannot be empty for game achievements."); + } + + Optional gameOptional = gameRepository.findByIdAndIsDeletedFalse(gameId); + + if (gameOptional.isEmpty()) { + throw new ResourceNotFoundException("Game with the given is not found."); + } + } else { + achievementRequestDto.setGameId("-1"); + } + + Achievement achievement = modelMapper.map(achievementRequestDto, Achievement.class); + + return achievementRepository.save(achievement); + } + + public Achievement updateAchievement(String id, UpdateAchievementRequestDto requestDto) { + Optional achievementOptional = achievementRepository.findByIdAndIsDeletedFalse(id); + + if (achievementOptional.isEmpty()) { + throw new ResourceNotFoundException("Achievement with the given id is not found."); + } + + Achievement achievementToUpdate = achievementOptional.get(); + + if (!requestDto.getIcon().isEmpty()) { + achievementToUpdate.setIcon(requestDto.getIcon()); + } + + if (!requestDto.getDescription().isEmpty()) { + achievementToUpdate.setDescription(requestDto.getDescription()); + } + + if (!requestDto.getTitle().isEmpty()) { + achievementToUpdate.setTitle(requestDto.getTitle()); + } + + achievementRepository.save(achievementToUpdate); + return achievementToUpdate; + } + + public Achievement deleteAchievement(String id) { + Optional achievementOptional = achievementRepository.findByIdAndIsDeletedFalse(id); + + if (achievementOptional.isEmpty()) { + throw new ResourceNotFoundException("Achievement with the given id is not found."); + } + + Achievement achievementToDelete = achievementOptional.get(); + + achievementToDelete.setIsDeleted(true); + + achievementRepository.save(achievementToDelete); + return achievementToDelete; + } +} From a730166f805412e6f867d1a12690c6b4013bc7cf Mon Sep 17 00:00:00 2001 From: Deniz Unal Date: Wed, 22 Nov 2023 22:45:53 +0300 Subject: [PATCH 3/7] added grant-achievement endpoint --- .../controller/AchievementController.java | 8 +++-- .../GrantAchievementRequestDto.java | 22 ++++++++++++ .../java/com/app/gamereview/model/User.java | 6 +++- .../service/AchievementService.java | 36 ++++++++++++++++++- 4 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 app/backend/src/main/java/com/app/gamereview/dto/request/achievement/GrantAchievementRequestDto.java diff --git a/app/backend/src/main/java/com/app/gamereview/controller/AchievementController.java b/app/backend/src/main/java/com/app/gamereview/controller/AchievementController.java index 57386e6d..c79796b7 100644 --- a/app/backend/src/main/java/com/app/gamereview/controller/AchievementController.java +++ b/app/backend/src/main/java/com/app/gamereview/controller/AchievementController.java @@ -1,6 +1,7 @@ package com.app.gamereview.controller; import com.app.gamereview.dto.request.achievement.CreateAchievementRequestDto; +import com.app.gamereview.dto.request.achievement.GrantAchievementRequestDto; import com.app.gamereview.dto.request.achievement.UpdateAchievementRequestDto; import com.app.gamereview.service.AchievementService; import com.app.gamereview.util.validation.annotation.AdminRequired; @@ -59,7 +60,10 @@ public ResponseEntity> getGameAchievements() { //TODO @PostMapping("/grant-achievement") - public ResponseEntity grantAchievement() { - return ResponseEntity.ok(new Achievement()); + @AdminRequired + public ResponseEntity> grantAchievement(@Valid @RequestBody GrantAchievementRequestDto achievementDto, + @RequestHeader String Authorization, HttpServletRequest request) { + List userAchievements = achievementService.grantAchievement(achievementDto); + return ResponseEntity.ok(userAchievements); } } diff --git a/app/backend/src/main/java/com/app/gamereview/dto/request/achievement/GrantAchievementRequestDto.java b/app/backend/src/main/java/com/app/gamereview/dto/request/achievement/GrantAchievementRequestDto.java new file mode 100644 index 00000000..b99f801b --- /dev/null +++ b/app/backend/src/main/java/com/app/gamereview/dto/request/achievement/GrantAchievementRequestDto.java @@ -0,0 +1,22 @@ +package com.app.gamereview.dto.request.achievement; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Pattern; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class GrantAchievementRequestDto { + @NotEmpty(message = "User id cannot be empty.") + @Pattern(regexp = "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$", + message = "User has invalid Id (UUID) format") + private String userId; + + @NotEmpty(message = "Achievement id cannot be empty.") + @Pattern(regexp = "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$", + message = "Achievement has invalid Id (UUID) format") + private String achievementId; +} diff --git a/app/backend/src/main/java/com/app/gamereview/model/User.java b/app/backend/src/main/java/com/app/gamereview/model/User.java index 7cbc155c..0be2f731 100644 --- a/app/backend/src/main/java/com/app/gamereview/model/User.java +++ b/app/backend/src/main/java/com/app/gamereview/model/User.java @@ -7,6 +7,9 @@ import org.springframework.data.annotation.TypeAlias; import org.springframework.data.mongodb.core.mapping.Document; +import java.util.ArrayList; +import java.util.List; + @Document(collection = "User") @TypeAlias("User") @Getter @@ -23,6 +26,8 @@ public class User extends BaseModel { private Boolean isVerified; + private List achievements = new ArrayList<>(); + public String getUsername() { return username; } @@ -63,5 +68,4 @@ public void setVerified(Boolean verified) { isVerified = verified; } - } diff --git a/app/backend/src/main/java/com/app/gamereview/service/AchievementService.java b/app/backend/src/main/java/com/app/gamereview/service/AchievementService.java index 13a85274..50816251 100644 --- a/app/backend/src/main/java/com/app/gamereview/service/AchievementService.java +++ b/app/backend/src/main/java/com/app/gamereview/service/AchievementService.java @@ -1,17 +1,21 @@ package com.app.gamereview.service; import com.app.gamereview.dto.request.achievement.CreateAchievementRequestDto; +import com.app.gamereview.dto.request.achievement.GrantAchievementRequestDto; import com.app.gamereview.dto.request.achievement.UpdateAchievementRequestDto; import com.app.gamereview.enums.AchievementType; import com.app.gamereview.exception.BadRequestException; import com.app.gamereview.exception.ResourceNotFoundException; import com.app.gamereview.model.Achievement; import com.app.gamereview.model.Game; +import com.app.gamereview.model.User; import com.app.gamereview.repository.AchievementRepository; import com.app.gamereview.repository.GameRepository; +import com.app.gamereview.repository.UserRepository; import org.modelmapper.ModelMapper; import org.springframework.stereotype.Service; +import java.util.List; import java.util.Objects; import java.util.Optional; @@ -22,11 +26,14 @@ public class AchievementService { private final GameRepository gameRepository; + private final UserRepository userRepository; + private final ModelMapper modelMapper; - public AchievementService(AchievementRepository achievementRepository, GameRepository gameRepository, ModelMapper modelMapper) { + public AchievementService(AchievementRepository achievementRepository, GameRepository gameRepository, UserRepository userRepository, ModelMapper modelMapper) { this.achievementRepository = achievementRepository; this.gameRepository = gameRepository; + this.userRepository = userRepository; this.modelMapper = modelMapper; } @@ -99,4 +106,31 @@ public Achievement deleteAchievement(String id) { achievementRepository.save(achievementToDelete); return achievementToDelete; } + + public List grantAchievement(GrantAchievementRequestDto request) { + Optional achievementOptional = achievementRepository.findByIdAndIsDeletedFalse(request.getAchievementId()); + + if (achievementOptional.isEmpty()) { + throw new ResourceNotFoundException("Achievement with the given id is not found."); + } + + Optional userOptional = userRepository.findByIdAndIsDeletedFalse(request.getUserId()); + + if (userOptional.isEmpty()) { + throw new ResourceNotFoundException("User with the given id is not found."); + } + + User userToGrant = userOptional.get(); + + List userAchievements = userToGrant.getAchievements(); + + if (userAchievements.contains(request.getAchievementId())) { + throw new BadRequestException("User already has the given achievement."); + } + + userToGrant.getAchievements().add(request.getAchievementId()); + + userRepository.save(userToGrant); + return userToGrant.getAchievements(); + } } From 848b5b12c1f31743914b84d98b3bf2b501cd611b Mon Sep 17 00:00:00 2001 From: Deniz Unal Date: Wed, 22 Nov 2023 23:47:42 +0300 Subject: [PATCH 4/7] added get-game-achievements endpoint --- .../gamereview/controller/AchievementController.java | 8 +++----- .../gamereview/repository/AchievementRepository.java | 5 +++++ .../app/gamereview/service/AchievementService.java | 11 +++++++++++ 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/app/backend/src/main/java/com/app/gamereview/controller/AchievementController.java b/app/backend/src/main/java/com/app/gamereview/controller/AchievementController.java index c79796b7..08495a92 100644 --- a/app/backend/src/main/java/com/app/gamereview/controller/AchievementController.java +++ b/app/backend/src/main/java/com/app/gamereview/controller/AchievementController.java @@ -13,7 +13,6 @@ import com.app.gamereview.model.Achievement; -import java.util.ArrayList; import java.util.List; @RestController @@ -52,13 +51,12 @@ public ResponseEntity deleteAchievement(@RequestParam String id, @R return ResponseEntity.ok(achievement); } - //TODO @GetMapping("/get-game-achievements") - public ResponseEntity> getGameAchievements() { - return ResponseEntity.ok(new ArrayList()); + public ResponseEntity> getGameAchievements(@RequestParam String gameId) { + List gameAchievements = achievementService.getGameAchievements(gameId); + return ResponseEntity.ok(gameAchievements); } - //TODO @PostMapping("/grant-achievement") @AdminRequired public ResponseEntity> grantAchievement(@Valid @RequestBody GrantAchievementRequestDto achievementDto, diff --git a/app/backend/src/main/java/com/app/gamereview/repository/AchievementRepository.java b/app/backend/src/main/java/com/app/gamereview/repository/AchievementRepository.java index 1f9e19ef..854f4cc7 100644 --- a/app/backend/src/main/java/com/app/gamereview/repository/AchievementRepository.java +++ b/app/backend/src/main/java/com/app/gamereview/repository/AchievementRepository.java @@ -3,6 +3,7 @@ import com.app.gamereview.model.Achievement; import org.springframework.data.mongodb.repository.MongoRepository; +import java.util.List; import java.util.Optional; public interface AchievementRepository extends MongoRepository { @@ -10,4 +11,8 @@ public interface AchievementRepository extends MongoRepository findByTitleAndIsDeletedFalse(String title); Optional findByIdAndIsDeletedFalse(String id); + + List findByGameAndIsDeletedFalse(String game); + + int countByGame(String game); } diff --git a/app/backend/src/main/java/com/app/gamereview/service/AchievementService.java b/app/backend/src/main/java/com/app/gamereview/service/AchievementService.java index 50816251..0882827b 100644 --- a/app/backend/src/main/java/com/app/gamereview/service/AchievementService.java +++ b/app/backend/src/main/java/com/app/gamereview/service/AchievementService.java @@ -133,4 +133,15 @@ public List grantAchievement(GrantAchievementRequestDto request) { userRepository.save(userToGrant); return userToGrant.getAchievements(); } + + public List getGameAchievements(String gameId) { + + Optional gameOptional = gameRepository.findByIdAndIsDeletedFalse(gameId); + + if (gameOptional.isEmpty()) { + throw new ResourceNotFoundException("Game with the given is not found."); + } + + return achievementRepository.findByGameAndIsDeletedFalse(gameId); + } } From a41f5d901d261726113c7a7f8c4229eb6b854fd7 Mon Sep 17 00:00:00 2001 From: Deniz Unal Date: Thu, 23 Nov 2023 00:00:29 +0300 Subject: [PATCH 5/7] added achievements to post entity and related endpoints --- .../dto/request/post/CreatePostRequestDto.java | 5 ++++- .../dto/response/post/GetPostDetailResponseDto.java | 4 ++-- .../dto/response/post/GetPostListResponseDto.java | 1 - .../src/main/java/com/app/gamereview/model/Post.java | 3 ++- .../java/com/app/gamereview/service/PostService.java | 11 ++++++++++- 5 files changed, 18 insertions(+), 6 deletions(-) diff --git a/app/backend/src/main/java/com/app/gamereview/dto/request/post/CreatePostRequestDto.java b/app/backend/src/main/java/com/app/gamereview/dto/request/post/CreatePostRequestDto.java index 4ae7a88d..8745b480 100644 --- a/app/backend/src/main/java/com/app/gamereview/dto/request/post/CreatePostRequestDto.java +++ b/app/backend/src/main/java/com/app/gamereview/dto/request/post/CreatePostRequestDto.java @@ -33,5 +33,8 @@ public class CreatePostRequestDto { message = "One of the tags has invalid Id (UUID) format")String> tags; // TODO annotations - // TODO achievements + + @Pattern(regexp = "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$", + message = "Achievement has invalid Id (UUID) format") + private String achievement; } diff --git a/app/backend/src/main/java/com/app/gamereview/dto/response/post/GetPostDetailResponseDto.java b/app/backend/src/main/java/com/app/gamereview/dto/response/post/GetPostDetailResponseDto.java index 7886f2a9..0495c928 100644 --- a/app/backend/src/main/java/com/app/gamereview/dto/response/post/GetPostDetailResponseDto.java +++ b/app/backend/src/main/java/com/app/gamereview/dto/response/post/GetPostDetailResponseDto.java @@ -2,7 +2,6 @@ import com.app.gamereview.model.Tag; import com.app.gamereview.model.User; -import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -23,11 +22,12 @@ public class GetPostDetailResponseDto { private User poster; - @NotNull private String forum; // TODO avatar + private String achievement; + private LocalDateTime lastEditedAt; private List tags; diff --git a/app/backend/src/main/java/com/app/gamereview/dto/response/post/GetPostListResponseDto.java b/app/backend/src/main/java/com/app/gamereview/dto/response/post/GetPostListResponseDto.java index 90365ae0..170573ed 100644 --- a/app/backend/src/main/java/com/app/gamereview/dto/response/post/GetPostListResponseDto.java +++ b/app/backend/src/main/java/com/app/gamereview/dto/response/post/GetPostListResponseDto.java @@ -46,5 +46,4 @@ public class GetPostListResponseDto { // TODO reports // TODO comments // TODO annotations - // TODO achievements } diff --git a/app/backend/src/main/java/com/app/gamereview/model/Post.java b/app/backend/src/main/java/com/app/gamereview/model/Post.java index d94b04ad..d5ef027d 100644 --- a/app/backend/src/main/java/com/app/gamereview/model/Post.java +++ b/app/backend/src/main/java/com/app/gamereview/model/Post.java @@ -48,7 +48,8 @@ public class Post extends BaseModel { // TODO reports // TODO annotations - // TODO achievements + + private String achievement; public void addVote(VoteChoice choice){ voteCount += 1; diff --git a/app/backend/src/main/java/com/app/gamereview/service/PostService.java b/app/backend/src/main/java/com/app/gamereview/service/PostService.java index 3fb79725..48f04735 100644 --- a/app/backend/src/main/java/com/app/gamereview/service/PostService.java +++ b/app/backend/src/main/java/com/app/gamereview/service/PostService.java @@ -42,17 +42,20 @@ public class PostService { private final VoteRepository voteRepository; + private final AchievementRepository achievementRepository; + private final MongoTemplate mongoTemplate; private final ModelMapper modelMapper; @Autowired - public PostService(PostRepository postRepository, ForumRepository forumRepository, UserRepository userRepository, TagRepository tagRepository, CommentRepository commentRepository, VoteRepository voteRepository, MongoTemplate mongoTemplate, ModelMapper modelMapper) { + public PostService(PostRepository postRepository, ForumRepository forumRepository, UserRepository userRepository, TagRepository tagRepository, CommentRepository commentRepository, VoteRepository voteRepository, AchievementRepository achievementRepository, MongoTemplate mongoTemplate, ModelMapper modelMapper) { this.postRepository = postRepository; this.forumRepository = forumRepository; this.userRepository = userRepository; this.tagRepository = tagRepository; this.commentRepository = commentRepository; this.voteRepository = voteRepository; + this.achievementRepository = achievementRepository; this.mongoTemplate = mongoTemplate; this.modelMapper = modelMapper; } @@ -225,6 +228,12 @@ public Post createPost(CreatePostRequestDto request, User user) { throw new ResourceNotFoundException("Forum is not found."); } + Optional achievementOptional = achievementRepository.findById(request.getAchievement()); + + if (achievementOptional.isEmpty()) { + throw new ResourceNotFoundException("Achievement is not found."); + } + if (request.getTags() != null) { for (String tagId : request.getTags()) { Optional tag = tagRepository.findById(tagId); From 804198d3b42b74ed16e7943501dac5acef4521f3 Mon Sep 17 00:00:00 2001 From: Deniz Unal Date: Thu, 23 Nov 2023 01:46:02 +0300 Subject: [PATCH 6/7] bugfixes related to newly added endpoints --- .../controller/AchievementController.java | 5 +++ .../CreateAchievementRequestDto.java | 7 ++-- .../UpdateAchievementRequestDto.java | 4 -- .../com/app/gamereview/model/Achievement.java | 2 +- .../repository/AchievementRepository.java | 2 +- .../service/AchievementService.java | 39 ++++++++++++------- 6 files changed, 36 insertions(+), 23 deletions(-) diff --git a/app/backend/src/main/java/com/app/gamereview/controller/AchievementController.java b/app/backend/src/main/java/com/app/gamereview/controller/AchievementController.java index 08495a92..0beefd00 100644 --- a/app/backend/src/main/java/com/app/gamereview/controller/AchievementController.java +++ b/app/backend/src/main/java/com/app/gamereview/controller/AchievementController.java @@ -5,6 +5,7 @@ import com.app.gamereview.dto.request.achievement.UpdateAchievementRequestDto; import com.app.gamereview.service.AchievementService; import com.app.gamereview.util.validation.annotation.AdminRequired; +import com.app.gamereview.util.validation.annotation.AuthorizationRequired; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import org.springframework.http.ResponseEntity; @@ -27,6 +28,7 @@ public AchievementController(AchievementService achievementService) { } @PostMapping("/create") + @AuthorizationRequired @AdminRequired public ResponseEntity createAchievement(@Valid @RequestBody CreateAchievementRequestDto achievementDto, @RequestHeader String Authorization, HttpServletRequest request) { @@ -35,6 +37,7 @@ public ResponseEntity createAchievement(@Valid @RequestBody CreateA } @PostMapping("/update") + @AuthorizationRequired @AdminRequired public ResponseEntity updateAchievement(@RequestParam String id, @Valid @RequestBody UpdateAchievementRequestDto updateAchievementRequestDto, @@ -44,6 +47,7 @@ public ResponseEntity updateAchievement(@RequestParam String id, } @DeleteMapping("/delete") + @AuthorizationRequired @AdminRequired public ResponseEntity deleteAchievement(@RequestParam String id, @RequestHeader String Authorization, HttpServletRequest request) { @@ -58,6 +62,7 @@ public ResponseEntity> getGameAchievements(@RequestParam Strin } @PostMapping("/grant-achievement") + @AuthorizationRequired @AdminRequired public ResponseEntity> grantAchievement(@Valid @RequestBody GrantAchievementRequestDto achievementDto, @RequestHeader String Authorization, HttpServletRequest request) { diff --git a/app/backend/src/main/java/com/app/gamereview/dto/request/achievement/CreateAchievementRequestDto.java b/app/backend/src/main/java/com/app/gamereview/dto/request/achievement/CreateAchievementRequestDto.java index 8d04bd55..00c94a66 100644 --- a/app/backend/src/main/java/com/app/gamereview/dto/request/achievement/CreateAchievementRequestDto.java +++ b/app/backend/src/main/java/com/app/gamereview/dto/request/achievement/CreateAchievementRequestDto.java @@ -3,6 +3,7 @@ import com.app.gamereview.enums.AchievementType; import com.app.gamereview.util.validation.annotation.ValidAchievementType; import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; import lombok.AllArgsConstructor; import lombok.Data; @@ -23,10 +24,10 @@ public class CreateAchievementRequestDto { private String icon; @ValidAchievementType(allowedValues = {AchievementType.GAME, AchievementType.META}) - @NotEmpty(message = "Achievement type cannot be empty.") - private AchievementType type; + @NotNull(message = "Achievement type cannot be empty.") + private String type; @Pattern(regexp = "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$", message = "Game has invalid Id (UUID) format") - private String gameId; + private String game; } diff --git a/app/backend/src/main/java/com/app/gamereview/dto/request/achievement/UpdateAchievementRequestDto.java b/app/backend/src/main/java/com/app/gamereview/dto/request/achievement/UpdateAchievementRequestDto.java index 92805045..128a3d47 100644 --- a/app/backend/src/main/java/com/app/gamereview/dto/request/achievement/UpdateAchievementRequestDto.java +++ b/app/backend/src/main/java/com/app/gamereview/dto/request/achievement/UpdateAchievementRequestDto.java @@ -1,6 +1,5 @@ package com.app.gamereview.dto.request.achievement; -import jakarta.validation.constraints.NotBlank; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -10,12 +9,9 @@ @NoArgsConstructor public class UpdateAchievementRequestDto { - @NotBlank(message = "Game title cannot be blank.") private String title; - @NotBlank(message = "Game description cannot be blank.") private String description; - @NotBlank(message = "Game icon cannot be blank.") private String icon; } diff --git a/app/backend/src/main/java/com/app/gamereview/model/Achievement.java b/app/backend/src/main/java/com/app/gamereview/model/Achievement.java index 919de8c7..8517f511 100644 --- a/app/backend/src/main/java/com/app/gamereview/model/Achievement.java +++ b/app/backend/src/main/java/com/app/gamereview/model/Achievement.java @@ -23,5 +23,5 @@ public class Achievement extends BaseModel { private AchievementType type; - private String gameId; + private String game; } diff --git a/app/backend/src/main/java/com/app/gamereview/repository/AchievementRepository.java b/app/backend/src/main/java/com/app/gamereview/repository/AchievementRepository.java index 854f4cc7..90ebbaa3 100644 --- a/app/backend/src/main/java/com/app/gamereview/repository/AchievementRepository.java +++ b/app/backend/src/main/java/com/app/gamereview/repository/AchievementRepository.java @@ -8,7 +8,7 @@ public interface AchievementRepository extends MongoRepository { - Optional findByTitleAndIsDeletedFalse(String title); + List findByTitleAndIsDeletedFalse(String title); Optional findByIdAndIsDeletedFalse(String id); diff --git a/app/backend/src/main/java/com/app/gamereview/service/AchievementService.java b/app/backend/src/main/java/com/app/gamereview/service/AchievementService.java index 0882827b..2aaeaaa9 100644 --- a/app/backend/src/main/java/com/app/gamereview/service/AchievementService.java +++ b/app/backend/src/main/java/com/app/gamereview/service/AchievementService.java @@ -16,7 +16,6 @@ import org.springframework.stereotype.Service; import java.util.List; -import java.util.Objects; import java.util.Optional; @Service @@ -39,17 +38,11 @@ public AchievementService(AchievementRepository achievementRepository, GameRepos public Achievement createAchievement(CreateAchievementRequestDto achievementRequestDto) { - Optional achievementOptional = achievementRepository.findByTitleAndIsDeletedFalse(achievementRequestDto.getTitle()); + if (achievementRequestDto.getType().equals(AchievementType.GAME.toString())) { - if (achievementOptional.isPresent() && Objects.equals(achievementOptional.get().getGameId(), achievementRequestDto.getGameId())) { - throw new BadRequestException("There is already an achievement with the same name."); - } - - if (achievementRequestDto.getType() == AchievementType.GAME) { - - String gameId = achievementRequestDto.getGameId(); + String gameId = achievementRequestDto.getGame(); - if (gameId.isEmpty()) { + if (gameId == null) { throw new BadRequestException("Game id cannot be empty for game achievements."); } @@ -59,7 +52,15 @@ public Achievement createAchievement(CreateAchievementRequestDto achievementRequ throw new ResourceNotFoundException("Game with the given is not found."); } } else { - achievementRequestDto.setGameId("-1"); + achievementRequestDto.setGame("-1"); + } + + List achievementsSameName = achievementRepository.findByTitleAndIsDeletedFalse(achievementRequestDto.getTitle()); + + for (Achievement achievement : achievementsSameName) { + if (achievement.getGame().equals(achievementRequestDto.getGame())) { + throw new BadRequestException("There is already an achievement with the same name."); + } } Achievement achievement = modelMapper.map(achievementRequestDto, Achievement.class); @@ -76,15 +77,25 @@ public Achievement updateAchievement(String id, UpdateAchievementRequestDto requ Achievement achievementToUpdate = achievementOptional.get(); - if (!requestDto.getIcon().isEmpty()) { + if (requestDto.getIcon() != null) { achievementToUpdate.setIcon(requestDto.getIcon()); } - if (!requestDto.getDescription().isEmpty()) { + if (requestDto.getDescription() != null) { achievementToUpdate.setDescription(requestDto.getDescription()); } - if (!requestDto.getTitle().isEmpty()) { + if (requestDto.getTitle() != null) { + + List achievementsSameName = achievementRepository.findByTitleAndIsDeletedFalse(requestDto.getTitle()); + + for (Achievement achievement : achievementsSameName) { + if (achievement.getGame().equals(achievementToUpdate.getGame()) && + !achievement.getId().equals(achievementToUpdate.getId())) { + throw new BadRequestException("There is already an achievement with the same name."); + } + } + achievementToUpdate.setTitle(requestDto.getTitle()); } From 2c6e03505487a2bc63947fbcf94cd15bc4e59c6f Mon Sep 17 00:00:00 2001 From: Deniz Unal Date: Thu, 23 Nov 2023 01:51:44 +0300 Subject: [PATCH 7/7] populated achievement in get-post-detail endpoint --- .../dto/response/post/GetPostDetailResponseDto.java | 3 ++- .../src/main/java/com/app/gamereview/service/PostService.java | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/backend/src/main/java/com/app/gamereview/dto/response/post/GetPostDetailResponseDto.java b/app/backend/src/main/java/com/app/gamereview/dto/response/post/GetPostDetailResponseDto.java index 0495c928..c1c7f10e 100644 --- a/app/backend/src/main/java/com/app/gamereview/dto/response/post/GetPostDetailResponseDto.java +++ b/app/backend/src/main/java/com/app/gamereview/dto/response/post/GetPostDetailResponseDto.java @@ -1,5 +1,6 @@ package com.app.gamereview.dto.response.post; +import com.app.gamereview.model.Achievement; import com.app.gamereview.model.Tag; import com.app.gamereview.model.User; import lombok.AllArgsConstructor; @@ -26,7 +27,7 @@ public class GetPostDetailResponseDto { // TODO avatar - private String achievement; + private Achievement achievement; private LocalDateTime lastEditedAt; diff --git a/app/backend/src/main/java/com/app/gamereview/service/PostService.java b/app/backend/src/main/java/com/app/gamereview/service/PostService.java index 48f04735..83c34e2a 100644 --- a/app/backend/src/main/java/com/app/gamereview/service/PostService.java +++ b/app/backend/src/main/java/com/app/gamereview/service/PostService.java @@ -140,6 +140,10 @@ public GetPostDetailResponseDto getPostById(String id, User user) { GetPostDetailResponseDto postDto = modelMapper.map(post, GetPostDetailResponseDto.class); + Optional postAchievement = achievementRepository.findByIdAndIsDeletedFalse(post.get().getAchievement()); + + postAchievement.ifPresent(postDto::setAchievement); + List tags = new ArrayList<>(); // Fetch tags individually