diff --git a/app/backend/src/main/java/com/app/gamereview/controller/CharacterController.java b/app/backend/src/main/java/com/app/gamereview/controller/CharacterController.java new file mode 100644 index 00000000..0142dbf0 --- /dev/null +++ b/app/backend/src/main/java/com/app/gamereview/controller/CharacterController.java @@ -0,0 +1,61 @@ +package com.app.gamereview.controller; + +import com.app.gamereview.dto.request.character.CreateCharacterRequestDto; +import com.app.gamereview.dto.request.character.UpdateCharacterRequestDto; +import com.app.gamereview.model.Character; +import com.app.gamereview.service.CharacterService; +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; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/api/character") +@Validated +public class CharacterController { + + private final CharacterService characterService; + + public CharacterController(CharacterService characterService) { + this.characterService = characterService; + } + + @PostMapping("/create") + @AuthorizationRequired + @AdminRequired + public ResponseEntity createCharacter(@Valid @RequestBody CreateCharacterRequestDto characterDto, + @RequestHeader String Authorization, HttpServletRequest request) { + Character character = characterService.createCharacter(characterDto); + return ResponseEntity.ok(character); + } + + @PostMapping("/update") + @AuthorizationRequired + @AdminRequired + public ResponseEntity updateCharacter(@RequestParam String id, + @Valid @RequestBody UpdateCharacterRequestDto updateCharacterRequestDto, + @RequestHeader String Authorization, HttpServletRequest request) { + Character character = characterService.updateCharacter(id, updateCharacterRequestDto); + return ResponseEntity.ok(character); + } + + @DeleteMapping("/delete") + @AuthorizationRequired + @AdminRequired + public ResponseEntity deleteCharacter(@RequestParam String id, @RequestHeader String Authorization, + HttpServletRequest request) { + Character character = characterService.deleteCharacter(id); + return ResponseEntity.ok(character); + } + + @GetMapping("/get-game-characters") + public ResponseEntity> getGameCharacters(@RequestParam String gameId) { + List gameCharacters = characterService.getGameCharacters(gameId); + return ResponseEntity.ok(gameCharacters); + } +} diff --git a/app/backend/src/main/java/com/app/gamereview/dto/request/character/CreateCharacterRequestDto.java b/app/backend/src/main/java/com/app/gamereview/dto/request/character/CreateCharacterRequestDto.java new file mode 100644 index 00000000..b49934ea --- /dev/null +++ b/app/backend/src/main/java/com/app/gamereview/dto/request/character/CreateCharacterRequestDto.java @@ -0,0 +1,46 @@ +package com.app.gamereview.dto.request.character; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Pattern; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; +import java.util.Map; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CreateCharacterRequestDto { + + @NotEmpty(message = "Character name cannot be empty.") + private String name; + + @NotEmpty(message = "Character icon cannot be empty.") + private String icon; + + @NotEmpty(message = "Game list cannot be empty.") + private List<@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")String> games; + + private String type; + + private String gender; + + private String race; + + private String status; + + private String occupation; + + private String birthDate; + + private String voiceActor; + + private String height; + + private String age; + + private Map customFields; +} diff --git a/app/backend/src/main/java/com/app/gamereview/dto/request/character/UpdateCharacterRequestDto.java b/app/backend/src/main/java/com/app/gamereview/dto/request/character/UpdateCharacterRequestDto.java new file mode 100644 index 00000000..e73bf719 --- /dev/null +++ b/app/backend/src/main/java/com/app/gamereview/dto/request/character/UpdateCharacterRequestDto.java @@ -0,0 +1,42 @@ +package com.app.gamereview.dto.request.character; + +import jakarta.validation.constraints.Pattern; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; +import java.util.Map; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class UpdateCharacterRequestDto { + + private String name; + + private String icon; + + private List<@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")String> games; + + private String type; + + private String gender; + + private String race; + + private String status; + + private String occupation; + + private String birthDate; + + private String voiceActor; + + private String height; + + private String age; + + private Map customFields; +} diff --git a/app/backend/src/main/java/com/app/gamereview/model/Character.java b/app/backend/src/main/java/com/app/gamereview/model/Character.java index 1836c27d..e19cc4ef 100644 --- a/app/backend/src/main/java/com/app/gamereview/model/Character.java +++ b/app/backend/src/main/java/com/app/gamereview/model/Character.java @@ -1,6 +1,5 @@ package com.app.gamereview.model; -import com.app.gamereview.enums.AchievementType; import com.app.gamereview.model.common.BaseModel; import lombok.AllArgsConstructor; import lombok.Getter; @@ -38,6 +37,10 @@ public class Character extends BaseModel { private String voiceActor; + private String height; + + private String age; + @Field("customFields") - private Map customFields; + private Map customFields; } diff --git a/app/backend/src/main/java/com/app/gamereview/repository/CharacterRepository.java b/app/backend/src/main/java/com/app/gamereview/repository/CharacterRepository.java index 438da628..520c0112 100644 --- a/app/backend/src/main/java/com/app/gamereview/repository/CharacterRepository.java +++ b/app/backend/src/main/java/com/app/gamereview/repository/CharacterRepository.java @@ -3,5 +3,13 @@ import com.app.gamereview.model.Character; import org.springframework.data.mongodb.repository.MongoRepository; +import java.util.List; +import java.util.Optional; + public interface CharacterRepository extends MongoRepository { + + Optional findByIdAndIsDeletedFalse(String id); + + List findByGamesContains(String game); + } diff --git a/app/backend/src/main/java/com/app/gamereview/service/CharacterService.java b/app/backend/src/main/java/com/app/gamereview/service/CharacterService.java new file mode 100644 index 00000000..bea34107 --- /dev/null +++ b/app/backend/src/main/java/com/app/gamereview/service/CharacterService.java @@ -0,0 +1,201 @@ +package com.app.gamereview.service; + +import com.app.gamereview.dto.request.character.UpdateCharacterRequestDto; +import com.app.gamereview.exception.BadRequestException; +import com.app.gamereview.exception.ResourceNotFoundException; +import com.app.gamereview.model.Game; +import com.app.gamereview.model.Character; +import com.app.gamereview.repository.CharacterRepository; +import com.app.gamereview.repository.GameRepository; +import com.app.gamereview.dto.request.character.CreateCharacterRequestDto; +import org.modelmapper.ModelMapper; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; + +@Service +public class CharacterService { + + private final CharacterRepository characterRepository; + + private final GameRepository gameRepository; + + private final ModelMapper modelMapper; + + public CharacterService(CharacterRepository characterRepository, GameRepository gameRepository, ModelMapper modelMapper) { + this.characterRepository = characterRepository; + this.gameRepository = gameRepository; + this.modelMapper = modelMapper; + } + + public Character createCharacter(CreateCharacterRequestDto characterRequestDto) { + + List games = characterRequestDto.getGames(); + + + if (games.isEmpty()) { + throw new BadRequestException("A character should have at least one game."); + } + + for (String gameId : games) { + Optional gameOptional = gameRepository.findByIdAndIsDeletedFalse(gameId); + + if (gameOptional.isEmpty()) { + throw new ResourceNotFoundException("Game with the given is not found."); + } + } + + Character characterToCreate = modelMapper.map(characterRequestDto, Character.class); + + return characterRepository.save(characterToCreate); + } + + public Character updateCharacter(String id, UpdateCharacterRequestDto requestDto) { + Optional characterOptional = characterRepository.findByIdAndIsDeletedFalse(id); + + if (characterOptional.isEmpty()) { + throw new ResourceNotFoundException("Character with the given id is not found."); + } + + Character characterToUpdate = characterOptional.get(); + + if (requestDto.getName() != null) { + if (requestDto.getName().isBlank()) { + throw new BadRequestException("Character name cannot be blank."); + } + characterToUpdate.setName(requestDto.getName()); + } + + if (requestDto.getIcon() != null) { + if (requestDto.getIcon().isBlank()) { + throw new BadRequestException("Character icon cannot be blank."); + } + characterToUpdate.setIcon(requestDto.getIcon()); + } + + if (requestDto.getGames() != null) { + + if (requestDto.getGames().isEmpty()) { + throw new BadRequestException("A character should have at least one game."); + } + + for (String gameId : requestDto.getGames()) { + Optional gameOptional = gameRepository.findByIdAndIsDeletedFalse(gameId); + + if (gameOptional.isEmpty()) { + throw new ResourceNotFoundException("Game with the given is not found."); + } + } + + characterToUpdate.setGames(requestDto.getGames()); + } + + if (requestDto.getType() != null) { + if (requestDto.getType().isBlank()) { + characterToUpdate.setType(null); + } else { + characterToUpdate.setType(requestDto.getType()); + } + } + + if (requestDto.getGender() != null) { + if (requestDto.getGender().isBlank()) { + characterToUpdate.setGender(null); + } else { + characterToUpdate.setGender(requestDto.getGender()); + } + } + + if (requestDto.getRace() != null) { + if (requestDto.getRace().isBlank()) { + characterToUpdate.setRace(null); + } else { + characterToUpdate.setRace(requestDto.getRace()); + } + } + + if (requestDto.getStatus() != null) { + if (requestDto.getStatus().isBlank()) { + characterToUpdate.setStatus(null); + } else { + characterToUpdate.setStatus(requestDto.getStatus()); + } + } + + if (requestDto.getOccupation() != null) { + if (requestDto.getOccupation().isBlank()) { + characterToUpdate.setOccupation(null); + } else { + characterToUpdate.setOccupation(requestDto.getOccupation()); + } + } + + if (requestDto.getBirthDate() != null) { + if (requestDto.getBirthDate().isBlank()) { + characterToUpdate.setBirthDate(null); + } else { + characterToUpdate.setBirthDate(requestDto.getBirthDate()); + } + } + + if (requestDto.getVoiceActor() != null) { + if (requestDto.getVoiceActor().isBlank()) { + characterToUpdate.setVoiceActor(null); + } else { + characterToUpdate.setVoiceActor(requestDto.getVoiceActor()); + } + } + + if (requestDto.getHeight() != null) { + if (requestDto.getHeight().isBlank()) { + characterToUpdate.setHeight(null); + } else { + characterToUpdate.setHeight(requestDto.getHeight()); + } + } + + if (requestDto.getAge() != null) { + if (requestDto.getAge().isBlank()) { + characterToUpdate.setAge(null); + } else { + characterToUpdate.setAge(requestDto.getAge()); + } + } + + if (requestDto.getCustomFields() != null) { + characterToUpdate.setCustomFields(requestDto.getCustomFields()); + } + + characterRepository.save(characterToUpdate); + return characterToUpdate; + } + + public Character deleteCharacter(String id) { + Optional characterOptional = characterRepository.findByIdAndIsDeletedFalse(id); + + if (characterOptional.isEmpty()) { + throw new ResourceNotFoundException("Character with the given id is not found."); + } + + Character characterToDelete = characterOptional.get(); + + characterToDelete.setIsDeleted(true); + + characterRepository.save(characterToDelete); + return characterToDelete; + } + + public List getGameCharacters(String gameId) { + + Optional gameOptional = gameRepository.findByIdAndIsDeletedFalse(gameId); + + if (gameOptional.isEmpty()) { + throw new ResourceNotFoundException("Game with the given is not found."); + } + + return characterRepository.findByGamesContains(gameId); + } + + +}