diff --git a/.github/workflows/Backend-CD-Prod.yml b/.github/workflows/Backend-CD-Prod-A.yml similarity index 84% rename from .github/workflows/Backend-CD-Prod.yml rename to .github/workflows/Backend-CD-Prod-A.yml index 5640a3a8..b3e8dc66 100644 --- a/.github/workflows/Backend-CD-Prod.yml +++ b/.github/workflows/Backend-CD-Prod-A.yml @@ -26,10 +26,6 @@ jobs: token: ${{ secrets.ACTION_TOKEN }} submodules: true - - name: Copy docker-compose.yml to home directory - working-directory: ./backend/pokerogue/src/main/resources - run: sudo cp ./docker-compose-prod.yml /home/ubuntu/docker-compose.yml - - name: Set up JDK 17 uses: actions/setup-java@v3 with: @@ -71,5 +67,7 @@ jobs: docker pull ${{ secrets.DOCKER_SERVER_IMAGE }} - docker-compose -f docker-compose.yml up -d server - docker image prune -f + docker run -d -p 80:8080 --name server \ + -e JAVA_OPTS="-XX:InitialRAMPercentage=70.0 -XX:MaxRAMPercentage=70.0" \ + -e TZ=Asia/Seoul \ + ${{ secrets.DOCKER_SERVER_IMAGE }} diff --git a/.github/workflows/Backend-CD-Prod-B.yml b/.github/workflows/Backend-CD-Prod-B.yml new file mode 100644 index 00000000..af02e421 --- /dev/null +++ b/.github/workflows/Backend-CD-Prod-B.yml @@ -0,0 +1,73 @@ +name: Backend Production Server CD + +on: + push: + branches: [ "be/release" ] + tags: + - 'v*' + +permissions: + contents: read + +jobs: + test: + uses: ./.github/workflows/Backend-CI.yml + secrets: inherit + + + build: + needs: test + runs-on: [cd, app-b] + steps: + + - name: Checkout + uses: actions/checkout@v3 + with: + token: ${{ secrets.ACTION_TOKEN }} + submodules: true + + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + + - name: Build with Gradle + run: ./gradlew bootJar + working-directory: ./backend/pokerogue + + - name: Docker build and push + run: | + docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} + + docker build -t ${{ secrets.DOCKER_SERVER_IMAGE }} -f ./backend/pokerogue/docker/Dockerfile ./backend/pokerogue + docker push ${{ secrets.DOCKER_SERVER_IMAGE }} + + + deploy: + needs: build + runs-on: [cd, app-b] + steps: + + - name: Change permission + run: | + sudo chown -R ubuntu:ubuntu /home/ubuntu/actions-runner/_work/2024-pokerogue-helper + + - name: Deploy + run: | + cd /home/ubuntu + + sudo chmod 666 /var/run/docker.sock + + if [ "$(docker ps -qa -f name=server)" ]; then + docker rm -f server + else + echo "No container named 'server' to remove." + fi + + docker pull ${{ secrets.DOCKER_SERVER_IMAGE }} + + docker run -d -p 80:8080 --name server \ + -e JAVA_OPTS="-XX:InitialRAMPercentage=70.0 -XX:MaxRAMPercentage=70.0" \ + -e TZ=Asia/Seoul \ + ${{ secrets.DOCKER_SERVER_IMAGE }} diff --git a/.github/workflows/Backend-CI.yml b/.github/workflows/Backend-CI.yml index 90be3077..f3a3c1fd 100644 --- a/.github/workflows/Backend-CI.yml +++ b/.github/workflows/Backend-CI.yml @@ -28,7 +28,7 @@ jobs: - name: Remove Containers run: | docker ps -aq | xargs -r docker rm -vf - docker builder prune + docker system prune -a -f - name: Set up Test MongoDB working-directory: ./backend/pokerogue/src/main/resources/ diff --git a/backend/pokerogue/src/main/java/com/pokerogue/PokerogueApplication.java b/backend/pokerogue/src/main/java/com/pokerogue/PokerogueApplication.java index e6976973..69b61604 100644 --- a/backend/pokerogue/src/main/java/com/pokerogue/PokerogueApplication.java +++ b/backend/pokerogue/src/main/java/com/pokerogue/PokerogueApplication.java @@ -2,14 +2,12 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; @SpringBootApplication -@EnableMongoRepositories public class PokerogueApplication { - public static void main(String[] args) { - SpringApplication.run(PokerogueApplication.class, args); - } + public static void main(String[] args) { + SpringApplication.run(PokerogueApplication.class, args); + } } diff --git a/backend/pokerogue/src/main/java/com/pokerogue/helper/biome/controller/BiomeController.java b/backend/pokerogue/src/main/java/com/pokerogue/helper/biome/controller/BiomeController.java index 11ebd300..efa26241 100644 --- a/backend/pokerogue/src/main/java/com/pokerogue/helper/biome/controller/BiomeController.java +++ b/backend/pokerogue/src/main/java/com/pokerogue/helper/biome/controller/BiomeController.java @@ -1,14 +1,16 @@ package com.pokerogue.helper.biome.controller; -import com.pokerogue.helper.biome.dto.BiomeResponse; import com.pokerogue.helper.biome.dto.BiomeDetailResponse; +import com.pokerogue.helper.biome.dto.BiomeResponse; import com.pokerogue.helper.biome.service.BiomeService; +import com.pokerogue.helper.global.constant.SortingCriteria; import com.pokerogue.helper.util.dto.ApiResponse; import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @Slf4j @@ -24,7 +26,9 @@ public ApiResponse> biomeList() { } @GetMapping("/api/v1/biome/{id}") - public ApiResponse biomeDetails(@PathVariable("id") String id) { + public ApiResponse biomeDetails(@PathVariable("id") String id, + @RequestParam(value = "boss", defaultValue = "desc") SortingCriteria bossPokemonOrder, + @RequestParam(value = "wild", defaultValue = "asc") SortingCriteria wildPokemonOrder) { log.info( "---- URI : {}, Param : {}, ThreadName : {}", "/api/v1/biome/{id}", @@ -32,6 +36,7 @@ public ApiResponse biomeDetails(@PathVariable("id") String Thread.currentThread().getName() ); - return new ApiResponse<>("바이옴 정보 불러오기에 성공했습니다.", biomeService.findBiome(id)); + return new ApiResponse<>("바이옴 정보 불러오기에 성공했습니다.", + biomeService.findBiome(id, bossPokemonOrder, wildPokemonOrder)); } } diff --git a/backend/pokerogue/src/main/java/com/pokerogue/helper/biome/converter/SortingCriteriaRequestConverter.java b/backend/pokerogue/src/main/java/com/pokerogue/helper/biome/converter/SortingCriteriaRequestConverter.java new file mode 100644 index 00000000..cb71a60f --- /dev/null +++ b/backend/pokerogue/src/main/java/com/pokerogue/helper/biome/converter/SortingCriteriaRequestConverter.java @@ -0,0 +1,12 @@ +package com.pokerogue.helper.biome.converter; + +import com.pokerogue.helper.global.constant.SortingCriteria; +import org.springframework.core.convert.converter.Converter; + +public class SortingCriteriaRequestConverter implements Converter { + + @Override + public SortingCriteria convert(String sortingCriteriaValue) { + return SortingCriteria.convertFrom(sortingCriteriaValue); + } +} diff --git a/backend/pokerogue/src/main/java/com/pokerogue/helper/biome/data/NativePokemon.java b/backend/pokerogue/src/main/java/com/pokerogue/helper/biome/data/NativePokemon.java index d65afa01..7ff6878d 100644 --- a/backend/pokerogue/src/main/java/com/pokerogue/helper/biome/data/NativePokemon.java +++ b/backend/pokerogue/src/main/java/com/pokerogue/helper/biome/data/NativePokemon.java @@ -2,16 +2,16 @@ import java.util.List; import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import org.springframework.data.mongodb.core.mapping.Field; @Getter +@AllArgsConstructor @NoArgsConstructor(access = AccessLevel.PROTECTED) public class NativePokemon { - private static final String BOSS = "보스"; - @Field("tier") private Tier tier; @@ -25,4 +25,8 @@ public boolean isWild() { public boolean isBoss() { return tier.isBoss(); } + + public int getRarity() { + return this.tier.getRarity(); + } } diff --git a/backend/pokerogue/src/main/java/com/pokerogue/helper/biome/data/Tier.java b/backend/pokerogue/src/main/java/com/pokerogue/helper/biome/data/Tier.java index 14274634..7b8d198d 100644 --- a/backend/pokerogue/src/main/java/com/pokerogue/helper/biome/data/Tier.java +++ b/backend/pokerogue/src/main/java/com/pokerogue/helper/biome/data/Tier.java @@ -8,21 +8,23 @@ @Getter public enum Tier { - COMMON("보통"), - UNCOMMON("드묾"), - RARE("레어"), - SUPER_RARE("슈퍼 레어"), - ULTRA_RARE("울트라 레어"), - BOSS("보스"), - BOSS_RARE("레어 보스"), - BOSS_SUPER_RARE("슈퍼 레어 보스"), - BOSS_ULTRA_RARE("슈퍼 울트라 레어 보스"), + COMMON("보통", 1), + UNCOMMON("드묾", 2), + RARE("레어", 3), + SUPER_RARE("슈퍼 레어", 4), + ULTRA_RARE("울트라 레어", 5), + BOSS("보스", 6), + BOSS_RARE("레어 보스", 7), + BOSS_SUPER_RARE("슈퍼 레어 보스", 8), + BOSS_ULTRA_RARE("슈퍼 울트라 레어 보스", 9), ; private final String name; + private final int rarity; - Tier(String name) { + Tier(String name, int rarity) { this.name = name; + this.rarity = rarity; } public boolean isWild() { diff --git a/backend/pokerogue/src/main/java/com/pokerogue/helper/biome/service/BiomeService.java b/backend/pokerogue/src/main/java/com/pokerogue/helper/biome/service/BiomeService.java index dd5741b9..e1c7d8c4 100644 --- a/backend/pokerogue/src/main/java/com/pokerogue/helper/biome/service/BiomeService.java +++ b/backend/pokerogue/src/main/java/com/pokerogue/helper/biome/service/BiomeService.java @@ -12,6 +12,7 @@ import com.pokerogue.helper.biome.dto.NextBiomeResponse; import com.pokerogue.helper.biome.dto.TrainerPokemonResponse; import com.pokerogue.helper.biome.repository.BiomeRepository; +import com.pokerogue.helper.global.constant.SortingCriteria; import com.pokerogue.helper.global.exception.ErrorMessage; import com.pokerogue.helper.global.exception.GlobalCustomException; import com.pokerogue.helper.pokemon.repository.PokemonRepository; @@ -39,32 +40,34 @@ public List findBiomes() { .toList(); } - public BiomeDetailResponse findBiome(String id) { + public BiomeDetailResponse findBiome(String id, SortingCriteria bossPokemonOrder, SortingCriteria wildPokemonOrder) { Biome biome = biomeRepository.findById(id) .orElseThrow(() -> new GlobalCustomException(ErrorMessage.BIOME_NOT_FOUND)); return BiomeDetailResponse.of( biome, s3Service.getBiomeImageFromS3(biome.getId()), - getWildPokemons(biome.getNativePokemons()), - getBossPokemons(biome.getNativePokemons()), + getWildPokemons(biome.getNativePokemons(), wildPokemonOrder), + getBossPokemons(biome.getNativePokemons(), bossPokemonOrder), getTrainerPokemons(biome), getNextBiomes(biome) ); } - private List getWildPokemons(List nativePokemons) { + private List getWildPokemons(List nativePokemons, SortingCriteria wildPokemonOrder) { return nativePokemons.stream() .filter(NativePokemon::isWild) + .sorted(NativePokemonComparator.of(wildPokemonOrder)) .map(nativePokemon -> BiomeAllPokemonResponse.of( nativePokemon, getBiomePokemons(nativePokemon.getPokemonIds()))) .toList(); } - private List getBossPokemons(List nativePokemons) { + private List getBossPokemons(List nativePokemons, SortingCriteria bossPokemonOrder) { return nativePokemons.stream() .filter(NativePokemon::isBoss) + .sorted(NativePokemonComparator.of(bossPokemonOrder)) .map(nativePokemon -> BiomeAllPokemonResponse.of( nativePokemon, getBiomePokemons(nativePokemon.getPokemonIds()))) diff --git a/backend/pokerogue/src/main/java/com/pokerogue/helper/biome/service/NativePokemonComparator.java b/backend/pokerogue/src/main/java/com/pokerogue/helper/biome/service/NativePokemonComparator.java new file mode 100644 index 00000000..24179731 --- /dev/null +++ b/backend/pokerogue/src/main/java/com/pokerogue/helper/biome/service/NativePokemonComparator.java @@ -0,0 +1,37 @@ +package com.pokerogue.helper.biome.service; + +import static com.pokerogue.helper.global.constant.SortingCriteria.ASCENDING; +import static com.pokerogue.helper.global.constant.SortingCriteria.DESCENDING; + +import com.pokerogue.helper.biome.data.NativePokemon; +import com.pokerogue.helper.global.constant.SortingCriteria; +import java.util.Comparator; + +public class NativePokemonComparator implements Comparator { + + private static final NativePokemonComparator ASCENDING_COMPARATOR = new NativePokemonComparator(ASCENDING); + private static final NativePokemonComparator DESCENDING_COMPARATOR = new NativePokemonComparator(DESCENDING); + + private final SortingCriteria criteria; + + private NativePokemonComparator(SortingCriteria criteria) { + this.criteria = criteria; + } + + public static NativePokemonComparator of(SortingCriteria criteria) { + if (criteria.equals(ASCENDING)) { + return ASCENDING_COMPARATOR; + } + + return DESCENDING_COMPARATOR; + } + + @Override + public int compare(NativePokemon firstPokemon, NativePokemon secondPokemon) { + if (this.criteria.equals(ASCENDING)) { + return Integer.compare(firstPokemon.getRarity(), secondPokemon.getRarity()); + } + + return Integer.compare(secondPokemon.getRarity(), firstPokemon.getRarity()); + } +} diff --git a/backend/pokerogue/src/main/java/com/pokerogue/helper/global/config/ConverterConfig.java b/backend/pokerogue/src/main/java/com/pokerogue/helper/global/config/DataMongoDbConfig.java similarity index 85% rename from backend/pokerogue/src/main/java/com/pokerogue/helper/global/config/ConverterConfig.java rename to backend/pokerogue/src/main/java/com/pokerogue/helper/global/config/DataMongoDbConfig.java index a6fad5dd..38d8e5aa 100644 --- a/backend/pokerogue/src/main/java/com/pokerogue/helper/global/config/ConverterConfig.java +++ b/backend/pokerogue/src/main/java/com/pokerogue/helper/global/config/DataMongoDbConfig.java @@ -10,9 +10,11 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.mongodb.core.convert.MongoCustomConversions; +import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; @Configuration -public class ConverterConfig { +@EnableMongoRepositories(basePackages = {"com.pokerogue"}) +public class DataMongoDbConfig { @Bean public MongoCustomConversions customConversions() { diff --git a/backend/pokerogue/src/main/java/com/pokerogue/helper/global/config/WebConfig.java b/backend/pokerogue/src/main/java/com/pokerogue/helper/global/config/WebConfig.java new file mode 100644 index 00000000..19d78c99 --- /dev/null +++ b/backend/pokerogue/src/main/java/com/pokerogue/helper/global/config/WebConfig.java @@ -0,0 +1,15 @@ +package com.pokerogue.helper.global.config; + +import com.pokerogue.helper.biome.converter.SortingCriteriaRequestConverter; +import org.springframework.context.annotation.Configuration; +import org.springframework.format.FormatterRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + + @Override + public void addFormatters(FormatterRegistry registry) { + registry.addConverter(new SortingCriteriaRequestConverter()); + } +} diff --git a/backend/pokerogue/src/main/java/com/pokerogue/helper/global/constant/SortingCriteria.java b/backend/pokerogue/src/main/java/com/pokerogue/helper/global/constant/SortingCriteria.java new file mode 100644 index 00000000..37a4693f --- /dev/null +++ b/backend/pokerogue/src/main/java/com/pokerogue/helper/global/constant/SortingCriteria.java @@ -0,0 +1,27 @@ +package com.pokerogue.helper.global.constant; + +import com.pokerogue.helper.global.exception.ErrorMessage; +import com.pokerogue.helper.global.exception.GlobalCustomException; +import java.util.Arrays; +import lombok.Getter; + +@Getter +public enum SortingCriteria { + + ASCENDING("asc"), + DESCENDING("desc"), + ; + + private final String value; + + SortingCriteria(String value) { + this.value = value; + } + + public static SortingCriteria convertFrom(String value) { + return Arrays.stream(values()) + .filter(criteria -> criteria.value.equals(value)) + .findAny() + .orElseThrow(() -> new GlobalCustomException(ErrorMessage.INVALID_SORTING_CRITERIA)); + } +} diff --git a/backend/pokerogue/src/main/java/com/pokerogue/helper/global/exception/ErrorMessage.java b/backend/pokerogue/src/main/java/com/pokerogue/helper/global/exception/ErrorMessage.java index 35701d42..e0551dba 100644 --- a/backend/pokerogue/src/main/java/com/pokerogue/helper/global/exception/ErrorMessage.java +++ b/backend/pokerogue/src/main/java/com/pokerogue/helper/global/exception/ErrorMessage.java @@ -41,9 +41,7 @@ public enum ErrorMessage { POKEMON_TYPE_DUPLICATION(HttpStatus.INTERNAL_SERVER_ERROR,"포켓몬의 타입은 중복될 수 없습니다."), POKEMON_EVOLUTION_ID_MISMATCH(HttpStatus.INTERNAL_SERVER_ERROR, "포켓몬과 진화 포켓몬 아이디가 서로 일치하지 않습니다"), - - - + INVALID_SORTING_CRITERIA(HttpStatus.BAD_REQUEST, "지원되지 않는 정렬 기준입니다."), FILE_ACCESS_FAILED(HttpStatus.BAD_REQUEST, "파일 정보 접근에 실패했습니다."), FILE_EXTENSION_NOT_APPLY(HttpStatus.BAD_REQUEST, "지원하지 않는 파일 형식입니다."), ; diff --git a/backend/pokerogue/src/main/java/com/pokerogue/helper/move/controller/MoveController.java b/backend/pokerogue/src/main/java/com/pokerogue/helper/move/controller/MoveController.java index b8c990ca..b0b2f3c0 100644 --- a/backend/pokerogue/src/main/java/com/pokerogue/helper/move/controller/MoveController.java +++ b/backend/pokerogue/src/main/java/com/pokerogue/helper/move/controller/MoveController.java @@ -1,5 +1,6 @@ package com.pokerogue.helper.move.controller; +import com.pokerogue.helper.move.dto.MoveDetailResponse; import com.pokerogue.helper.move.dto.MoveResponse; import com.pokerogue.helper.move.service.MoveService; import com.pokerogue.helper.util.dto.ApiResponse; @@ -18,6 +19,11 @@ public class MoveController { private final MoveService moveService; + @GetMapping("/api/v1/move/dex") + public ApiResponse> moveList() { + return new ApiResponse<>("기술 리스트 불러오기에 성공했습니다.", moveService.findMoves()); + } + @GetMapping("/api/v1/moves") public ApiResponse> moveListByPokedexNumber(@RequestParam("pokedex-number") Integer pokedexNumber) { log.info( @@ -31,7 +37,7 @@ public ApiResponse> moveListByPokedexNumber(@RequestParam("po } @GetMapping("/api/v1/move/{id}") - public ApiResponse moveDetails(@PathVariable String id) { + public ApiResponse moveDetailsInBattle(@PathVariable String id) { log.info( "---- URI : {}, Param : {}, ThreadName : {}", "/api/v1/move/{id}", @@ -39,6 +45,18 @@ public ApiResponse moveDetails(@PathVariable String id) { Thread.currentThread().getName() ); - return new ApiResponse<>("포켓몬의 기술 불러오기에 성공했습니다.", moveService.findMove(id)); + return new ApiResponse<>("기술 정보 불러오기에 성공했습니다.", moveService.findMoveInBattle(id)); + } + + @GetMapping("/api/v1/move/dex/{id}") + public ApiResponse moveDetails(@PathVariable String id) { + log.info( + "---- URI : {}, Param : {}, ThreadName : {}", + "/api/v1/move/dex/{id}", + id, + Thread.currentThread().getName() + ); + + return new ApiResponse<>("기술 정보 불러오기에 성공했습니다.", moveService.findMove(id)); } } diff --git a/backend/pokerogue/src/main/java/com/pokerogue/helper/move/data/MoveFlag.java b/backend/pokerogue/src/main/java/com/pokerogue/helper/move/data/MoveFlag.java index 6bafdf46..d9cb6b1a 100644 --- a/backend/pokerogue/src/main/java/com/pokerogue/helper/move/data/MoveFlag.java +++ b/backend/pokerogue/src/main/java/com/pokerogue/helper/move/data/MoveFlag.java @@ -3,7 +3,9 @@ import com.pokerogue.helper.global.exception.ErrorMessage; import com.pokerogue.helper.global.exception.GlobalCustomException; import java.util.Arrays; +import lombok.Getter; +@Getter public enum MoveFlag { NONE("none"), @@ -26,6 +28,7 @@ public enum MoveFlag { TRIAGE_MOVE("triage_move"), IGNORE_ABILITIES("ignore_abilities"), CHECK_ALL_HITS("check_all_hits"), + IGNORE_SUBSTITUTE("ignore_substitute"), REDIRECT_COUNTER("redirect_counter"), ; diff --git a/backend/pokerogue/src/main/java/com/pokerogue/helper/move/data/MoveTarget.java b/backend/pokerogue/src/main/java/com/pokerogue/helper/move/data/MoveTarget.java index ed098053..7c0d8801 100644 --- a/backend/pokerogue/src/main/java/com/pokerogue/helper/move/data/MoveTarget.java +++ b/backend/pokerogue/src/main/java/com/pokerogue/helper/move/data/MoveTarget.java @@ -3,7 +3,9 @@ import com.pokerogue.helper.global.exception.ErrorMessage; import com.pokerogue.helper.global.exception.GlobalCustomException; import java.util.Arrays; +import lombok.Getter; +@Getter public enum MoveTarget { USER("user"), diff --git a/backend/pokerogue/src/main/java/com/pokerogue/helper/move/dto/MoveDetailResponse.java b/backend/pokerogue/src/main/java/com/pokerogue/helper/move/dto/MoveDetailResponse.java new file mode 100644 index 00000000..08e14792 --- /dev/null +++ b/backend/pokerogue/src/main/java/com/pokerogue/helper/move/dto/MoveDetailResponse.java @@ -0,0 +1,58 @@ +package com.pokerogue.helper.move.dto; + +import com.pokerogue.helper.move.data.Move; +import com.pokerogue.helper.move.data.MoveCategory; +import com.pokerogue.helper.move.data.MoveFlag; +import com.pokerogue.helper.type.data.Type; +import java.util.List; + +public record MoveDetailResponse( + String id, + String name, + String typeEngName, + String typeLogo, + String categoryEngName, + String categoryLogo, + String moveTarget, + Integer power, + Integer accuracy, + Integer powerPoint, + String effect, + Integer effectChance, + Integer priority, + Integer generation, + String released, + List flags, + List pokemonIdsWithLevelMove, + List pokemonIdsWithEggMove +) { + + public static MoveDetailResponse from(Move move, List levelMoveIdsContains, List eggMoveIdsContains) { + Type type = move.getType(); + MoveCategory moveCategory = move.getMoveCategory(); + List moveFlags = move.getFlags().stream() + .map(MoveFlag::getId) + .toList(); + + return new MoveDetailResponse( + move.getId(), + move.getKoName(), + type.getName(), + type.getImage(), + moveCategory.getEngName(), + moveCategory.getImage(), + move.getMoveTarget().getId(), + move.getPower(), + move.getAccuracy(), + move.getPowerPoint(), + move.getEffect(), + move.getEffectChance(), + move.getPriority(), + move.getGeneration(), + move.getReleased(), + moveFlags, + levelMoveIdsContains, + eggMoveIdsContains + ); + } +} diff --git a/backend/pokerogue/src/main/java/com/pokerogue/helper/move/service/MoveService.java b/backend/pokerogue/src/main/java/com/pokerogue/helper/move/service/MoveService.java index adbea2d3..41e48abb 100644 --- a/backend/pokerogue/src/main/java/com/pokerogue/helper/move/service/MoveService.java +++ b/backend/pokerogue/src/main/java/com/pokerogue/helper/move/service/MoveService.java @@ -3,6 +3,7 @@ import com.pokerogue.helper.global.exception.ErrorMessage; import com.pokerogue.helper.global.exception.GlobalCustomException; import com.pokerogue.helper.move.data.Move; +import com.pokerogue.helper.move.dto.MoveDetailResponse; import com.pokerogue.helper.move.dto.MoveResponse; import com.pokerogue.helper.move.repository.MoveRepository; import com.pokerogue.helper.pokemon.data.LevelMove; @@ -20,6 +21,12 @@ public class MoveService { private final PokemonRepository pokemonRepository; private final MoveRepository moveRepository; + public List findMoves() { + return moveRepository.findAll().stream() + .map(MoveResponse::from) + .toList(); + } + public List findMovesByPokemon(Integer pokedexNumber) { List pokemons = pokemonRepository.findByPokedexNumber(pokedexNumber); if (pokemons.isEmpty()) { @@ -51,11 +58,23 @@ private static List getAllMoveIds(Pokemon pokemon) { return allMoveIds; } - public MoveResponse findMove(String id) { + public MoveResponse findMoveInBattle(String id) { Move move = findMoveById(id); return MoveResponse.from(move); } + public MoveDetailResponse findMove(String id) { + Move move = findMoveById(id); + List eggMovePokemonIds = pokemonRepository.findByEggMoveIdsContains(move.getId()).stream() + .map(Pokemon::getId) + .toList(); + List levelMovePokemonIds = pokemonRepository.findByLevelMovesMoveId(move.getId()).stream() + .map(Pokemon::getId) + .toList(); + + return MoveDetailResponse.from(move, levelMovePokemonIds, eggMovePokemonIds); + } + private Move findMoveById(String id) { return moveRepository.findById(id) .orElseThrow(() -> new GlobalCustomException(ErrorMessage.MOVE_NOT_FOUND)); diff --git a/backend/pokerogue/src/main/java/com/pokerogue/helper/pokemon/repository/PokemonRepository.java b/backend/pokerogue/src/main/java/com/pokerogue/helper/pokemon/repository/PokemonRepository.java index 05fc4d91..3bddbbc3 100644 --- a/backend/pokerogue/src/main/java/com/pokerogue/helper/pokemon/repository/PokemonRepository.java +++ b/backend/pokerogue/src/main/java/com/pokerogue/helper/pokemon/repository/PokemonRepository.java @@ -7,4 +7,8 @@ public interface PokemonRepository extends MongoRepository { List findByPokedexNumber(int pokedexNumber); + + List findByEggMoveIdsContains(String eggMoveIds); + + List findByLevelMovesMoveId(String moveId); } diff --git a/backend/pokerogue/src/main/resources b/backend/pokerogue/src/main/resources index 8d0ded1d..0a82ab4e 160000 --- a/backend/pokerogue/src/main/resources +++ b/backend/pokerogue/src/main/resources @@ -1 +1 @@ -Subproject commit 8d0ded1d3b1d34ca75fb46c924b96bc6da71deb0 +Subproject commit 0a82ab4eea07d1dfa2b65e835770802294a81911 diff --git a/backend/pokerogue/src/test/java/com/pokerogue/environment/controller/ControllerTest.java b/backend/pokerogue/src/test/java/com/pokerogue/environment/controller/ControllerTest.java new file mode 100644 index 00000000..2b7674b7 --- /dev/null +++ b/backend/pokerogue/src/test/java/com/pokerogue/environment/controller/ControllerTest.java @@ -0,0 +1,21 @@ +package com.pokerogue.environment.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.web.servlet.MockMvc; + +@ActiveProfiles("local") +public abstract class ControllerTest { + + @Autowired + protected MockMvc mockMvc; + + @Autowired + protected ObjectMapper objectMapper; + + @MockBean + protected MongoTemplate mongoTemplate; +} diff --git a/backend/pokerogue/src/test/java/com/pokerogue/environment/repository/MongoRepositoryTest.java b/backend/pokerogue/src/test/java/com/pokerogue/environment/repository/MongoRepositoryTest.java index 4d964877..0b3ce3ee 100644 --- a/backend/pokerogue/src/test/java/com/pokerogue/environment/repository/MongoRepositoryTest.java +++ b/backend/pokerogue/src/test/java/com/pokerogue/environment/repository/MongoRepositoryTest.java @@ -1,12 +1,12 @@ package com.pokerogue.environment.repository; -import com.pokerogue.helper.global.config.ConverterConfig; +import com.pokerogue.helper.global.config.DataMongoDbConfig; import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest; import org.springframework.context.annotation.Import; import org.springframework.test.context.ActiveProfiles; @DataMongoTest @ActiveProfiles("local") -@Import(ConverterConfig.class) +@Import(DataMongoDbConfig.class) public abstract class MongoRepositoryTest { } diff --git a/backend/pokerogue/src/test/java/com/pokerogue/helper/biome/controller/BiomeControllerTest.java b/backend/pokerogue/src/test/java/com/pokerogue/helper/biome/controller/BiomeControllerTest.java new file mode 100644 index 00000000..6c17faa2 --- /dev/null +++ b/backend/pokerogue/src/test/java/com/pokerogue/helper/biome/controller/BiomeControllerTest.java @@ -0,0 +1,58 @@ +package com.pokerogue.helper.biome.controller; + +import static org.mockito.Mockito.verify; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.pokerogue.environment.controller.ControllerTest; +import com.pokerogue.helper.biome.service.BiomeService; +import com.pokerogue.helper.global.constant.SortingCriteria; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; + +@WebMvcTest(value = BiomeController.class) +class BiomeControllerTest extends ControllerTest { + + @MockBean + private BiomeService biomeService; + + @Test + @DisplayName("바이옴 포켓몬 정렬 기준 쿼리 스트링을 바인딩한다.") + void bindBiomePokemonSortingCriteriaRequestParameter() throws Exception { + mockMvc.perform(get("/api/v1/biome/test_id") + .content(MediaType.APPLICATION_JSON_VALUE) + .param("boss", "asc").param("wild", "desc")) + .andDo(print()) + .andExpect(status().isOk()); + + verify(biomeService).findBiome("test_id", SortingCriteria.ASCENDING, SortingCriteria.DESCENDING); + } + + @Test + @DisplayName("바이옴 포켓몬 정렬 기준 쿼리 스트링을 설정하지 않았다면 기본값으로 바인딩한다.") + void bindDefaultBiomePokemonSortingCriteriaRequestParameter() throws Exception { + mockMvc.perform(get("/api/v1/biome/test_id") + .content(MediaType.APPLICATION_JSON_VALUE)) + .andDo(print()) + .andExpect(status().isOk()); + + verify(biomeService).findBiome("test_id", SortingCriteria.DESCENDING, SortingCriteria.ASCENDING); + } + + + @Test + @DisplayName("잘못된 값의 바이옴 포켓몬 정렬 기준 쿼리 스트링으로 요청하면 예외 응답을 반환한다.") + void handlesInvalidSortingCriteriaRequestParameter() throws Exception { + mockMvc.perform(get("/api/v1/biome/test_id") + .content(MediaType.APPLICATION_JSON_VALUE) + .param("boss", "ascc").param("wild", "desc")) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.detail").value("Failed to convert 'boss' with value: 'ascc'")); + } +} diff --git a/backend/pokerogue/src/test/java/com/pokerogue/helper/biome/service/BiomeServiceTest.java b/backend/pokerogue/src/test/java/com/pokerogue/helper/biome/service/BiomeServiceTest.java index bed2ec00..715bc735 100644 --- a/backend/pokerogue/src/test/java/com/pokerogue/helper/biome/service/BiomeServiceTest.java +++ b/backend/pokerogue/src/test/java/com/pokerogue/helper/biome/service/BiomeServiceTest.java @@ -1,12 +1,16 @@ package com.pokerogue.helper.biome.service; +import static com.pokerogue.helper.global.constant.SortingCriteria.ASCENDING; +import static com.pokerogue.helper.global.constant.SortingCriteria.DESCENDING; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; import com.pokerogue.environment.service.ServiceTest; +import com.pokerogue.helper.biome.dto.BiomeAllPokemonResponse; import com.pokerogue.helper.biome.dto.BiomeDetailResponse; import com.pokerogue.helper.biome.dto.BiomeResponse; +import com.pokerogue.helper.global.constant.SortingCriteria; import com.pokerogue.helper.global.exception.ErrorMessage; import com.pokerogue.helper.global.exception.GlobalCustomException; import java.util.List; @@ -30,7 +34,7 @@ void findBoimes() { @Test @DisplayName("단일 바이옴 정보를 불러온다") void findBiome() { - BiomeDetailResponse biomeDetailResponse = biomeService.findBiome("fairy_cave"); + BiomeDetailResponse biomeDetailResponse = biomeService.findBiome("fairy_cave", ASCENDING, DESCENDING); assertAll( () -> assertThat(biomeDetailResponse.id()).isEqualTo("fairy_cave"), @@ -45,8 +49,25 @@ void findBiome() { @Test @DisplayName("해당 id의 바이옴이 없는 경우 예외를 발생시킨다") void notExistBiome() { - assertThatThrownBy(() -> biomeService.findBiome("test")) + assertThatThrownBy(() -> biomeService.findBiome("test", ASCENDING, DESCENDING)) .isInstanceOf(GlobalCustomException.class) .hasMessage(ErrorMessage.BIOME_NOT_FOUND.getMessage()); } + + @Test + @DisplayName("바이옴 포켓몬의 티어를 희귀도 순으로 정렬한다.") + void sortBiomeNativePokemons() { + SortingCriteria bossPokemonOrder = DESCENDING; + SortingCriteria wildPokemonOrder = ASCENDING; + + BiomeDetailResponse biomeDetailResponse = biomeService.findBiome("fairy_cave", bossPokemonOrder, + wildPokemonOrder); + + assertAll(() -> { + assertThat(biomeDetailResponse.wildPokemons()).extracting(BiomeAllPokemonResponse::tier) + .containsExactly("보통", "드묾", "레어", "슈퍼 레어", "울트라 레어"); + assertThat(biomeDetailResponse.bossPokemons()).extracting(BiomeAllPokemonResponse::tier) + .containsExactly("슈퍼 울트라 레어 보스", "슈퍼 레어 보스", "레어 보스", "보스"); + }); + } } diff --git a/backend/pokerogue/src/test/java/com/pokerogue/helper/biome/service/NativePokemonComparatorTest.java b/backend/pokerogue/src/test/java/com/pokerogue/helper/biome/service/NativePokemonComparatorTest.java new file mode 100644 index 00000000..5ad7d7ae --- /dev/null +++ b/backend/pokerogue/src/test/java/com/pokerogue/helper/biome/service/NativePokemonComparatorTest.java @@ -0,0 +1,41 @@ +package com.pokerogue.helper.biome.service; + +import static com.pokerogue.helper.global.constant.SortingCriteria.ASCENDING; +import static com.pokerogue.helper.global.constant.SortingCriteria.DESCENDING; +import static org.assertj.core.api.Assertions.assertThat; + +import com.pokerogue.helper.biome.data.NativePokemon; +import com.pokerogue.helper.biome.data.Tier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class NativePokemonComparatorTest { + + private final List nativePokemons = new ArrayList<>(List.of( + new NativePokemon(Tier.RARE, List.of("bulbasaur", "venusaur")), + new NativePokemon(Tier.COMMON, List.of("pikachu", "raichu")), + new NativePokemon(Tier.BOSS_RARE, List.of("charmander")), + new NativePokemon(Tier.BOSS_SUPER_RARE, List.of("roserade")) + )); + + @Test + @DisplayName("바이옴 포켓몬을 티어의 희귀도가 낮은 순으로 정렬한다.") + void sortNativePokemonsAscending() { + Collections.sort(nativePokemons, NativePokemonComparator.of(ASCENDING)); + + assertThat(nativePokemons).extracting(NativePokemon::getTier) + .containsExactly(Tier.COMMON, Tier.RARE, Tier.BOSS_RARE, Tier.BOSS_SUPER_RARE); + } + + @Test + @DisplayName("바이옴 포켓몬을 티어의 희귀도가 높은 순으로 정렬한다.") + void sortNativePokemonsDescending() { + Collections.sort(nativePokemons, NativePokemonComparator.of(DESCENDING)); + + assertThat(nativePokemons).extracting(NativePokemon::getTier) + .containsExactly(Tier.BOSS_SUPER_RARE, Tier.BOSS_RARE, Tier.RARE, Tier.COMMON); + } +} diff --git a/backend/pokerogue/src/test/java/com/pokerogue/helper/global/constant/SortingCriteriaTest.java b/backend/pokerogue/src/test/java/com/pokerogue/helper/global/constant/SortingCriteriaTest.java new file mode 100644 index 00000000..f33dc3b7 --- /dev/null +++ b/backend/pokerogue/src/test/java/com/pokerogue/helper/global/constant/SortingCriteriaTest.java @@ -0,0 +1,28 @@ +package com.pokerogue.helper.global.constant; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class SortingCriteriaTest { + + @ParameterizedTest + @MethodSource("sortingCriteriaValues") + @DisplayName("문자열 값으로 SortingCriteria를 생성한다.") + void convertFrom(String value, SortingCriteria expected) { + SortingCriteria actual = SortingCriteria.convertFrom(value); + + assertThat(actual).isEqualTo(expected); + } + + private static Stream sortingCriteriaValues() { + return Stream.of( + Arguments.of("desc", SortingCriteria.DESCENDING), + Arguments.of("asc", SortingCriteria.ASCENDING) + ); + } +} diff --git a/backend/pokerogue/src/test/java/com/pokerogue/helper/move/service/MoveServiceTest.java b/backend/pokerogue/src/test/java/com/pokerogue/helper/move/service/MoveServiceTest.java index aeffda2f..5c870b58 100644 --- a/backend/pokerogue/src/test/java/com/pokerogue/helper/move/service/MoveServiceTest.java +++ b/backend/pokerogue/src/test/java/com/pokerogue/helper/move/service/MoveServiceTest.java @@ -7,8 +7,10 @@ import com.pokerogue.environment.service.ServiceTest; import com.pokerogue.helper.global.exception.ErrorMessage; import com.pokerogue.helper.global.exception.GlobalCustomException; +import com.pokerogue.helper.move.dto.MoveDetailResponse; import com.pokerogue.helper.move.dto.MoveResponse; import java.util.List; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -27,9 +29,9 @@ void findMovesByPokemon() { } @Test - @DisplayName("단일 기술 정보를 불러온다") - void findMove() { - MoveResponse moveResponse = moveService.findMove("earth_power"); + @DisplayName("배틀 서비스에서 사용할 단일 기술 정보를 불러온다") + void findMoveByBattle() { + MoveResponse moveResponse = moveService.findMoveInBattle("earth_power"); assertAll( () -> assertThat(moveResponse.id()).isEqualTo("earth_power"), @@ -47,8 +49,44 @@ void findMove() { @Test @DisplayName("id에 해당하는 기술이 없는 경우 예외를 발생시킨다") void notExistMove() { - assertThatThrownBy(() -> moveService.findMove("test")) + assertThatThrownBy(() -> moveService.findMoveInBattle("test")) .isInstanceOf(GlobalCustomException.class) .hasMessage(ErrorMessage.MOVE_NOT_FOUND.getMessage()); } + + @Test + @DisplayName("전체 기술 목록을 반환한다") + void findMoves() { + List movesByPokemon = moveService.findMoves(); + + assertThat(movesByPokemon.size()).isEqualTo(919); + } + + @Disabled + @Test + @DisplayName("단일 기술 정보를 불러온다") + void findMove() { + MoveDetailResponse moveDetailResponse = moveService.findMove("earth_power"); + + assertAll( + () -> assertThat(moveDetailResponse.id()).isEqualTo("earth_power"), + () -> assertThat(moveDetailResponse.name()).isEqualTo("대지의힘"), + () -> assertThat(moveDetailResponse.typeEngName()).isEqualTo("ground"), + () -> assertThat(moveDetailResponse.typeLogo()).contains("type/ground"), + () -> assertThat(moveDetailResponse.categoryEngName()).isEqualTo("special"), + () -> assertThat(moveDetailResponse.categoryLogo()).contains("move-category/special.png"), + () -> assertThat(moveDetailResponse.moveTarget()).isEqualTo("near_other"), + () -> assertThat(moveDetailResponse.power()).isEqualTo(90), + () -> assertThat(moveDetailResponse.accuracy()).isEqualTo(100), + () -> assertThat(moveDetailResponse.powerPoint()).isEqualTo(10), + () -> assertThat(moveDetailResponse.effect()).isEqualTo("상대의 발밑에 대지의 힘을 방출한다. 상대의 특수방어를 떨어뜨릴 때가 있다."), + () -> assertThat(moveDetailResponse.effectChance()).isEqualTo(10), + () -> assertThat(moveDetailResponse.priority()).isEqualTo(0), + () -> assertThat(moveDetailResponse.generation()).isEqualTo(4), + () -> assertThat(moveDetailResponse.released()).isNull(), + () -> assertThat(moveDetailResponse.flags()).isEmpty(), + () -> assertThat(moveDetailResponse.pokemonIdsWithLevelMove()).hasSize(71), + () -> assertThat(moveDetailResponse.pokemonIdsWithEggMove()).hasSize(117) + ); + } }