Skip to content

Commit

Permalink
Merge pull request #40 from OPGG-HACKTHON/feature/8-game-detail
Browse files Browse the repository at this point in the history
Feature/8 game detail
  • Loading branch information
gmlwo530 authored Aug 23, 2021
2 parents 77e7e3e + af3cfd4 commit 20e7998
Show file tree
Hide file tree
Showing 11 changed files with 428 additions and 68 deletions.
35 changes: 18 additions & 17 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,35 +1,36 @@
plugins {
id 'org.springframework.boot' version '2.5.3'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
id 'org.springframework.boot' version '2.5.3'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}

group = 'opgg.weba'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

configurations {
compileOnly {
extendsFrom annotationProcessor
}
compileOnly {
extendsFrom annotationProcessor
}
}

repositories {
mavenCentral()
mavenCentral()
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
runtimeOnly 'mysql:mysql-connector-java'
implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.5.6'
implementation group: 'org.qlrm', name: 'qlrm', version: '3.0.1'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation group: 'org.modelmapper', name: 'modelmapper', version: '2.4.2'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
runtimeOnly 'mysql:mysql-connector-java'
implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.5.6'
implementation group: 'org.qlrm', name: 'qlrm', version: '3.0.1'
}

test {
useJUnitPlatform()
useJUnitPlatform()
}
19 changes: 19 additions & 0 deletions src/main/java/opgg/weba/JamPick/config/JampickConfiguration.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package opgg.weba.JamPick.config;

import org.modelmapper.ModelMapper;
import org.modelmapper.convention.NamingConventions;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JampickConfiguration {
@Bean
public ModelMapper modelMapper() {
ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration()
.setFieldMatchingEnabled(true)
.setFieldAccessLevel(org.modelmapper.config.Configuration.AccessLevel.PRIVATE)
.setSourceNamingConvention(NamingConventions.JAVABEANS_MUTATOR);
return modelMapper;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package opgg.weba.JamPick.controller;

import opgg.weba.JamPick.dto.IndieAppDetailDto;
import opgg.weba.JamPick.dto.ResponseDto;
import opgg.weba.JamPick.service.IndieAppDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class IndieAppDetailController {

@Autowired
private IndieAppDetailService indieAppDetailService;

@GetMapping(value = "/detail/{indieAppId}")
public ResponseEntity<ResponseDto> getIndieAppDetail(@PathVariable("indieAppId") Long indieAppId) {
IndieAppDetailDto indieAppDetailDto = indieAppDetailService.getIndieAppDetail(indieAppId);

ResponseDto responseDto = ResponseDto.builder()
.status(200)
.responseMessage("상세 조회 성공")
.data(indieAppDetailDto)
.build();

return ResponseEntity.ok(responseDto);
}
}
29 changes: 29 additions & 0 deletions src/main/java/opgg/weba/JamPick/dto/IndieAppDetailDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package opgg.weba.JamPick.dto;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import opgg.weba.JamPick.domain.Genre;

import java.util.List;

@Getter
@Setter
@NoArgsConstructor
public class IndieAppDetailDto {
private Long id;

private String name;

private String shortDescription;

private String headerImage;

private String releaseDate;

private List<String> genres;

private List<String> screenshots;

private List<String> movies;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package opgg.weba.JamPick.repository;

import opgg.weba.JamPick.domain.IndieAppDetail;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.query.Param;

import java.util.Optional;

public interface IndieAppDetailRepository extends JpaRepository<IndieAppDetail, Long> {
Optional<IndieAppDetail> findByIndieApp_IdAndLanguage(@Param(value = "indieAppId") Long indieAppId, String language);
}
37 changes: 37 additions & 0 deletions src/main/java/opgg/weba/JamPick/repository/IndieAppRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package opgg.weba.JamPick.repository;

import opgg.weba.JamPick.domain.IndieApp;
import opgg.weba.JamPick.domain.IndieAppDetail;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;
import java.util.Optional;

public interface IndieAppRepository extends JpaRepository<IndieApp, Long> {

@Query(
value = "SELECT g.description FROM genre AS g" +
" WHERE g.language = :language AND g.indie_app_id = :id" +
" ORDER BY g.description",
nativeQuery = true
)
List<String> findGenres(@Param("id") Long indieAppId, @Param("language") String language);

@Query(
value = "SELECT m.mp4 FROM movie AS m" +
" WHERE m.indie_app_id = :id" +
" ORDER BY m.mp4",
nativeQuery = true
)
List<String> findMovies(@Param("id") Long indieAppId);

@Query(
value = "SELECT s.path_full FROM screenshot AS s" +
" WHERE s.indie_app_id = :id" +
" ORDER BY s.path_full",
nativeQuery = true
)
List<String> findScreenshots(@Param("id") Long indieAppId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package opgg.weba.JamPick.service;

import opgg.weba.JamPick.dto.IndieAppDetailDto;

public interface IndieAppDetailService {
IndieAppDetailDto getIndieAppDetail(Long id);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package opgg.weba.JamPick.service;

import opgg.weba.JamPick.common.GetLocale;
import opgg.weba.JamPick.domain.IndieApp;
import opgg.weba.JamPick.domain.IndieAppDetail;
import opgg.weba.JamPick.dto.IndieAppDetailDto;
import opgg.weba.JamPick.exception.EntityType;
import opgg.weba.JamPick.exception.ExceptionType;
import opgg.weba.JamPick.exception.JampickException;
import opgg.weba.JamPick.repository.IndieAppDetailRepository;
import opgg.weba.JamPick.repository.IndieAppRepository;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
public class IndieAppDetailServiceImpl implements IndieAppDetailService {

@Autowired
private IndieAppRepository indieAppRepository;

@Autowired
private IndieAppDetailRepository indieAppDetailRepository;

@Autowired
private ModelMapper modelMapper;

@Override
public IndieAppDetailDto getIndieAppDetail(Long indieAppId) {
String localeString = GetLocale.getLocale().toString();

Optional<IndieAppDetail> indieAppDetailOptional = indieAppDetailRepository.findByIndieApp_IdAndLanguage(indieAppId, localeString);

if (indieAppDetailOptional.isPresent()) {
IndieAppDetail indieAppDetail = indieAppDetailOptional.get();
IndieApp indieApp = indieAppDetail.getIndieApp();

IndieAppDetailDto indieAppDetailDto = modelMapper.map(indieAppDetail, IndieAppDetailDto.class);
indieAppDetailDto.setHeaderImage(indieApp.getHeaderImage());
indieAppDetailDto.setName(indieApp.getName());

indieAppDetailDto.setGenres(indieAppRepository.findGenres(indieAppId, localeString));
indieAppDetailDto.setMovies(indieAppRepository.findMovies(indieAppId));
indieAppDetailDto.setScreenshots(indieAppRepository.findScreenshots(indieAppId));

return indieAppDetailDto;
}

throw JampickException.throwException(EntityType.INDIE_APP_DETAIL, ExceptionType.ENTITY_NOT_FOUND, String.valueOf(indieAppId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package opgg.weba.JamPick.controller;

import opgg.weba.JamPick.domain.IndieApp;
import opgg.weba.JamPick.domain.IndieAppDetail;
import opgg.weba.JamPick.repository.IndieAppDetailRepository;
import opgg.weba.JamPick.repository.IndieAppRepository;
import opgg.weba.JamPick.util.Util;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import java.util.Locale;
import java.util.Optional;

import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.equalTo;
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;

@SpringBootTest
@Transactional
@AutoConfigureMockMvc
class IndieAppDetailControllerTest {

@Autowired
IndieAppRepository indieAppRepository;

@Autowired
IndieAppDetailRepository indieAppDetailRepository;

@Autowired
EntityManager em;

@Autowired
private MockMvc mockMvc;

@BeforeEach
void setUp() {
IndieApp indieApp = Util.createIndieApp(indieAppRepository, 1L);

Util.createIndieAppDetail(em, indieApp);

Util.createGenres(em, indieApp, 3);

Util.createMovies(em, indieApp, 3);

Util.createScreenshots(em, indieApp, 3);
}

@Test
void getIndieAppDetail() throws Exception {
Optional<IndieAppDetail> indieAppDetailOptional = indieAppDetailRepository.findByIndieApp_IdAndLanguage(1L, Locale.KOREA.toString());

assertThat(indieAppDetailOptional).isPresent();

IndieAppDetail indieAppDetail = indieAppDetailOptional.get();

mockMvc.perform(
get("/api/detail/1")
.locale(Locale.KOREA)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
)
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.id").value(equalTo(indieAppDetail.getId().intValue())))
.andExpect(jsonPath("$.data.name").value(equalTo(indieAppDetail.getIndieApp().getName())))
.andExpect(jsonPath("$.data.shortDescription").value(equalTo(indieAppDetail.getShortDescription())))
.andExpect(jsonPath("$.data.headerImage").value(equalTo(indieAppDetail.getIndieApp().getHeaderImage())))
.andExpect(jsonPath("$.data.releaseDate").value(equalTo(indieAppDetail.getReleaseDate())))
.andExpect(jsonPath("$.data.genres[0]").exists())
.andExpect(jsonPath("$.data.genres[1]").exists())
.andExpect(jsonPath("$.data.genres[2]").exists())
.andExpect(jsonPath("$.data.movies[0]").exists())
.andExpect(jsonPath("$.data.movies[1]").exists())
.andExpect(jsonPath("$.data.movies[2]").exists())
.andExpect(jsonPath("$.data.screenshots[0]").exists())
.andExpect(jsonPath("$.data.screenshots[1]").exists())
.andExpect(jsonPath("$.data.screenshots[2]").exists());


}
}
Loading

0 comments on commit 20e7998

Please sign in to comment.