diff --git a/pom.xml b/pom.xml
index 05c00f8..84da830 100644
--- a/pom.xml
+++ b/pom.xml
@@ -46,6 +46,12 @@
org.springframework.boot
spring-boot-starter-test
test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+
+
diff --git a/src/main/java/org/takiguchi/demo/intellijshortcuts/controller/GameController.java b/src/main/java/org/takiguchi/demo/intellijshortcuts/controller/GameController.java
new file mode 100644
index 0000000..6ef9d14
--- /dev/null
+++ b/src/main/java/org/takiguchi/demo/intellijshortcuts/controller/GameController.java
@@ -0,0 +1,24 @@
+package org.takiguchi.demo.intellijshortcuts.controller;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.takiguchi.demo.intellijshortcuts.model.Game;
+import org.takiguchi.demo.intellijshortcuts.service.GameService;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/games")
+public class GameController {
+ private final GameService gameService;
+
+ public GameController(GameService gameService) {
+ this.gameService = gameService;
+ }
+
+ @GetMapping
+ public List getAll() {
+ return gameService.findAll();
+ }
+}
diff --git a/src/main/java/org/takiguchi/demo/intellijshortcuts/model/Dlc.java b/src/main/java/org/takiguchi/demo/intellijshortcuts/model/Dlc.java
new file mode 100644
index 0000000..d15fd09
--- /dev/null
+++ b/src/main/java/org/takiguchi/demo/intellijshortcuts/model/Dlc.java
@@ -0,0 +1,28 @@
+package org.takiguchi.demo.intellijshortcuts.model;
+
+import lombok.*;
+
+import javax.persistence.*;
+import java.time.LocalDate;
+import java.util.UUID;
+
+import static javax.persistence.FetchType.LAZY;
+
+@Entity
+@Table(name = "dlc")
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class Dlc {
+ @Id
+ @GeneratedValue(generator = "system-uuid")
+ private UUID id;
+ private String title;
+ private LocalDate publicationDate;
+ private Double price;
+ @ManyToOne(fetch = LAZY)
+ @JoinColumn(name = "game_id")
+ private Game game;
+}
diff --git a/src/main/java/org/takiguchi/demo/intellijshortcuts/model/Editor.java b/src/main/java/org/takiguchi/demo/intellijshortcuts/model/Editor.java
new file mode 100644
index 0000000..4ff7b71
--- /dev/null
+++ b/src/main/java/org/takiguchi/demo/intellijshortcuts/model/Editor.java
@@ -0,0 +1,25 @@
+package org.takiguchi.demo.intellijshortcuts.model;
+
+import lombok.*;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import java.time.LocalDate;
+import java.util.UUID;
+
+@Entity
+@Table(name = "editor")
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class Editor {
+ @Id
+ @GeneratedValue(generator = "system-uuid")
+ private UUID id;
+ private String name;
+ private LocalDate creationDate;
+}
diff --git a/src/main/java/org/takiguchi/demo/intellijshortcuts/model/Game.java b/src/main/java/org/takiguchi/demo/intellijshortcuts/model/Game.java
new file mode 100644
index 0000000..cf45a62
--- /dev/null
+++ b/src/main/java/org/takiguchi/demo/intellijshortcuts/model/Game.java
@@ -0,0 +1,33 @@
+package org.takiguchi.demo.intellijshortcuts.model;
+
+import lombok.*;
+
+import javax.persistence.*;
+import java.time.LocalDate;
+import java.util.List;
+import java.util.UUID;
+
+import static javax.persistence.CascadeType.*;
+import static javax.persistence.FetchType.LAZY;
+
+@Entity
+@Table(name = "game")
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class Game {
+ @Id
+ @GeneratedValue(generator = "system-uuid")
+ private UUID id;
+ private String title;
+ private String description;
+ private LocalDate publicationDate;
+ private Double price;
+ @ManyToOne(fetch = LAZY)
+ @JoinColumn(name = "editor_id")
+ private Editor editor;
+ @OneToMany(mappedBy = "game", cascade = ALL)
+ private List dlcList;
+}
diff --git a/src/main/java/org/takiguchi/demo/intellijshortcuts/repository/DlcRepository.java b/src/main/java/org/takiguchi/demo/intellijshortcuts/repository/DlcRepository.java
new file mode 100644
index 0000000..96b137d
--- /dev/null
+++ b/src/main/java/org/takiguchi/demo/intellijshortcuts/repository/DlcRepository.java
@@ -0,0 +1,12 @@
+package org.takiguchi.demo.intellijshortcuts.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+import org.takiguchi.demo.intellijshortcuts.model.Dlc;
+
+import java.util.UUID;
+
+@Repository
+public interface DlcRepository extends JpaRepository {
+
+}
diff --git a/src/main/java/org/takiguchi/demo/intellijshortcuts/repository/EditorRepository.java b/src/main/java/org/takiguchi/demo/intellijshortcuts/repository/EditorRepository.java
new file mode 100644
index 0000000..afd90fb
--- /dev/null
+++ b/src/main/java/org/takiguchi/demo/intellijshortcuts/repository/EditorRepository.java
@@ -0,0 +1,11 @@
+package org.takiguchi.demo.intellijshortcuts.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+import org.takiguchi.demo.intellijshortcuts.model.Editor;
+
+import java.util.UUID;
+
+@Repository
+public interface EditorRepository extends JpaRepository {
+}
diff --git a/src/main/java/org/takiguchi/demo/intellijshortcuts/repository/GameRepository.java b/src/main/java/org/takiguchi/demo/intellijshortcuts/repository/GameRepository.java
new file mode 100644
index 0000000..927e275
--- /dev/null
+++ b/src/main/java/org/takiguchi/demo/intellijshortcuts/repository/GameRepository.java
@@ -0,0 +1,15 @@
+package org.takiguchi.demo.intellijshortcuts.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+import org.takiguchi.demo.intellijshortcuts.model.Game;
+
+import java.util.List;
+import java.util.UUID;
+
+@Repository
+public interface GameRepository extends JpaRepository {
+ @Query("SELECT DISTINCT g FROM Game g LEFT JOIN FETCH g.dlcList")
+ List findAllWithDlcs();
+}
diff --git a/src/main/java/org/takiguchi/demo/intellijshortcuts/service/GameService.java b/src/main/java/org/takiguchi/demo/intellijshortcuts/service/GameService.java
new file mode 100644
index 0000000..47e7f00
--- /dev/null
+++ b/src/main/java/org/takiguchi/demo/intellijshortcuts/service/GameService.java
@@ -0,0 +1,20 @@
+package org.takiguchi.demo.intellijshortcuts.service;
+
+import org.springframework.stereotype.Service;
+import org.takiguchi.demo.intellijshortcuts.model.Game;
+import org.takiguchi.demo.intellijshortcuts.repository.GameRepository;
+
+import java.util.List;
+
+@Service
+public class GameService {
+ private final GameRepository gameRepository;
+
+ public GameService(GameRepository gameRepository) {
+ this.gameRepository = gameRepository;
+ }
+
+ public List findAll() {
+ return gameRepository.findAllWithDlcs();
+ }
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.yml
similarity index 100%
rename from src/main/resources/application.properties
rename to src/main/resources/application.yml
diff --git a/src/test/java/org/takiguchi/demo/intellijshortcuts/configuration/RepositoryConfiguration.java b/src/test/java/org/takiguchi/demo/intellijshortcuts/configuration/RepositoryConfiguration.java
new file mode 100644
index 0000000..a333455
--- /dev/null
+++ b/src/test/java/org/takiguchi/demo/intellijshortcuts/configuration/RepositoryConfiguration.java
@@ -0,0 +1,37 @@
+package org.takiguchi.demo.intellijshortcuts.configuration;
+
+import org.springframework.boot.test.context.TestConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
+import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
+import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
+import org.takiguchi.demo.intellijshortcuts.repository.EditorRepository;
+import org.takiguchi.demo.intellijshortcuts.repository.GameRepository;
+import org.takiguchi.demo.intellijshortcuts.util.DatabaseInitializer;
+
+import javax.sql.DataSource;
+
+@TestConfiguration
+@ComponentScan(basePackages = "org.takiguchi.demo.intellijshortcuts.repository")
+public class RepositoryConfiguration {
+ @Bean
+ public EmbeddedDatabase dataSource() {
+ return new EmbeddedDatabaseBuilder()
+ .setType(EmbeddedDatabaseType.H2)
+ .addScript("sql/01_init-db.sql")
+ .build();
+ }
+
+ @Bean
+ public JdbcTemplate jdbcTemplate(DataSource dataSource) {
+ return new JdbcTemplate(dataSource);
+ }
+
+ @Bean
+ public DatabaseInitializer dataConfiguration(EditorRepository editorRepository,
+ GameRepository gameRepository) {
+ return new DatabaseInitializer(editorRepository, gameRepository);
+ }
+}
diff --git a/src/test/java/org/takiguchi/demo/intellijshortcuts/configuration/TestUtilsConfiguration.java b/src/test/java/org/takiguchi/demo/intellijshortcuts/configuration/TestUtilsConfiguration.java
new file mode 100644
index 0000000..f6cbd32
--- /dev/null
+++ b/src/test/java/org/takiguchi/demo/intellijshortcuts/configuration/TestUtilsConfiguration.java
@@ -0,0 +1,16 @@
+package org.takiguchi.demo.intellijshortcuts.configuration;
+
+import org.springframework.boot.test.context.TestConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.util.DefaultUriBuilderFactory;
+
+@TestConfiguration
+public class TestUtilsConfiguration {
+ @Bean
+ public RestTemplate restTemplate() {
+ RestTemplate restTemplate = new RestTemplate();
+ restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory("http://localhost:8080"));
+ return restTemplate;
+ }
+}
diff --git a/src/test/java/org/takiguchi/demo/intellijshortcuts/controller/GameControllerTest.java b/src/test/java/org/takiguchi/demo/intellijshortcuts/controller/GameControllerTest.java
new file mode 100644
index 0000000..120c5d1
--- /dev/null
+++ b/src/test/java/org/takiguchi/demo/intellijshortcuts/controller/GameControllerTest.java
@@ -0,0 +1,49 @@
+package org.takiguchi.demo.intellijshortcuts.controller;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.takiguchi.demo.intellijshortcuts.configuration.RepositoryConfiguration;
+import org.takiguchi.demo.intellijshortcuts.configuration.TestUtilsConfiguration;
+import org.takiguchi.demo.intellijshortcuts.extension.DatabaseInitializationExtension;
+import org.takiguchi.demo.intellijshortcuts.model.Dlc;
+import org.takiguchi.demo.intellijshortcuts.model.Game;
+
+import java.util.Collection;
+import java.util.List;
+
+import static java.util.stream.Collectors.toList;
+import static org.assertj.core.api.Assertions.assertThat;
+
+@ExtendWith({
+ SpringExtension.class,
+ DatabaseInitializationExtension.class
+})
+@SpringBootTest(classes = {
+ RepositoryConfiguration.class,
+ TestUtilsConfiguration.class
+})
+public class GameControllerTest {
+ @Autowired
+ private GameController gameController;
+
+ @Test
+ void getAll_should_fetch_dlc_list() {
+ // when
+ List games = gameController.getAll();
+
+ // then
+ assertThat(games).hasSize(5);
+ List dlcTitles = games.stream()
+ .filter(game -> "World of Warcraft".equals(game.getTitle()))
+ .findFirst()
+ .map(Game::getDlcList)
+ .stream()
+ .flatMap(Collection::stream)
+ .map(Dlc::getTitle)
+ .collect(toList());
+ assertThat(dlcTitles).containsExactlyInAnyOrder("The Burning Crusade", "Wrath of the Lich King");
+ }
+}
diff --git a/src/test/java/org/takiguchi/demo/intellijshortcuts/extension/DatabaseInitializationExtension.java b/src/test/java/org/takiguchi/demo/intellijshortcuts/extension/DatabaseInitializationExtension.java
new file mode 100644
index 0000000..cac28cb
--- /dev/null
+++ b/src/test/java/org/takiguchi/demo/intellijshortcuts/extension/DatabaseInitializationExtension.java
@@ -0,0 +1,24 @@
+package org.takiguchi.demo.intellijshortcuts.extension;
+
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.springframework.context.ApplicationContext;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.takiguchi.demo.intellijshortcuts.util.DatabaseInitializer;
+
+public class DatabaseInitializationExtension implements BeforeAllCallback, AfterAllCallback {
+ @Override
+ public void beforeAll(ExtensionContext extensionContext) {
+ ApplicationContext applicationContext = SpringExtension.getApplicationContext(extensionContext);
+ DatabaseInitializer databaseInitializer = applicationContext.getBean(DatabaseInitializer.class);
+ databaseInitializer.initData();
+ }
+
+ @Override
+ public void afterAll(ExtensionContext extensionContext) {
+ ApplicationContext applicationContext = SpringExtension.getApplicationContext(extensionContext);
+ DatabaseInitializer databaseInitializer = applicationContext.getBean(DatabaseInitializer.class);
+ databaseInitializer.clearData();
+ }
+}
diff --git a/src/test/java/org/takiguchi/demo/intellijshortcuts/util/DatabaseInitializer.java b/src/test/java/org/takiguchi/demo/intellijshortcuts/util/DatabaseInitializer.java
new file mode 100644
index 0000000..de0dc72
--- /dev/null
+++ b/src/test/java/org/takiguchi/demo/intellijshortcuts/util/DatabaseInitializer.java
@@ -0,0 +1,119 @@
+package org.takiguchi.demo.intellijshortcuts.util;
+
+import org.takiguchi.demo.intellijshortcuts.model.Dlc;
+import org.takiguchi.demo.intellijshortcuts.model.Editor;
+import org.takiguchi.demo.intellijshortcuts.model.Game;
+import org.takiguchi.demo.intellijshortcuts.repository.EditorRepository;
+import org.takiguchi.demo.intellijshortcuts.repository.GameRepository;
+
+import java.time.LocalDate;
+import java.util.List;
+
+public class DatabaseInitializer {
+ private final EditorRepository editorRepository;
+ private final GameRepository gameRepository;
+
+ public DatabaseInitializer(EditorRepository editorRepository,
+ GameRepository gameRepository) {
+ this.editorRepository = editorRepository;
+ this.gameRepository = gameRepository;
+ }
+
+ public void initData() {
+ Editor psyconix = Editor.builder()
+ .name("Psyconix")
+ .creationDate(LocalDate.of(2000, 4, 30))
+ .build();
+
+ Editor codeMasters = Editor.builder()
+ .name("Codemasters")
+ .creationDate(LocalDate.of(1986, 1, 2))
+ .build();
+
+ Editor blizzard = Editor.builder()
+ .name("Blizzard")
+ .creationDate(LocalDate.of(1991, 2, 8))
+ .build();
+
+ editorRepository.saveAll(List.of(
+ psyconix,
+ codeMasters,
+ blizzard
+ ));
+
+ Game rocketLeague = Game.builder()
+ .title("Rocket league")
+ .description("Rocket League est un mélange de jeu de voiture et de jeu de football.")
+ .publicationDate(LocalDate.of(2015, 7, 7))
+ .price(0D)
+ .editor(psyconix)
+ .build();
+
+ Game f1_2019 = Game.builder()
+ .title("F1 2019")
+ .description("Jeu de simulation de courses de F1 pour la saison 2019.")
+ .publicationDate(LocalDate.of(2019, 6, 28))
+ .price(6.99)
+ .editor(codeMasters)
+ .build();
+
+ Game f1_2020 = Game.builder()
+ .title("F1 2020")
+ .description("Jeu de simulation de courses de F1 pour la saison 2020.")
+ .publicationDate(LocalDate.of(2019, 7, 10))
+ .price(6.99)
+ .editor(codeMasters)
+ .build();
+
+ List warcraft3DlcList = List.of(Dlc.builder()
+ .title("Warcraft III: The Frozen Throne.")
+ .publicationDate(LocalDate.of(2003, 7, 1))
+ .price(14.99)
+ .build());
+ Game warcraft3 = Game.builder()
+ .title("Warcraft III")
+ .description("Jeu de stratégie en temps réel dans l'univers de Warcraft.")
+ .publicationDate(LocalDate.of(2002, 7, 3))
+ .price(49.99)
+ .editor(blizzard)
+ .dlcList(warcraft3DlcList)
+ .build();
+ warcraft3DlcList.forEach(dlc -> dlc.setGame(warcraft3));
+
+ List wowDlcList = List.of(
+ Dlc.builder()
+ .title("The Burning Crusade")
+ .publicationDate(LocalDate.of(2007, 1, 16))
+ .price(14.99)
+ .build(),
+ Dlc.builder()
+ .title("Wrath of the Lich King")
+ .publicationDate(LocalDate.of(2008, 11, 13))
+ .price(14.99)
+ .build()
+ );
+ Game worldOfWarcraft = Game.builder()
+ .title("World of Warcraft")
+ .description("MMORPG basé sur l'univers de Warcraft")
+ .publicationDate(LocalDate.of(2004, 11, 23))
+ .price(59.99)
+ .editor(blizzard)
+ .dlcList(wowDlcList)
+ .build();
+ wowDlcList.forEach(dlc -> dlc.setGame(worldOfWarcraft));
+
+ gameRepository.saveAll(List.of(
+ rocketLeague,
+ f1_2019,
+ f1_2020,
+ warcraft3,
+ worldOfWarcraft
+ ));
+ }
+
+ public void clearData() {
+// dlcRepository.deleteAll();
+ gameRepository.deleteAll();
+ editorRepository.deleteAll();
+ }
+}
diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml
new file mode 100644
index 0000000..e69de29
diff --git a/src/test/resources/sql/01_init-db.sql b/src/test/resources/sql/01_init-db.sql
new file mode 100644
index 0000000..a4bd691
--- /dev/null
+++ b/src/test/resources/sql/01_init-db.sql
@@ -0,0 +1,30 @@
+CREATE TABLE editor (
+ id UUID DEFAULT random_uuid() NOT NULL,
+ name VARCHAR NOT NULL,
+ creation_date DATE NOT NULL,
+ CONSTRAINT pk_editor PRIMARY KEY (id)
+);
+
+CREATE TABLE game (
+ id UUID DEFAULT random_uuid() NOT NULL,
+ title VARCHAR NOT NULL,
+ description VARCHAR NOT NULL,
+ publication_date DATE NOT NULL,
+ price NUMBER NOT NULL DEFAULT 0,
+ editor_id UUID NOT NULL,
+ CONSTRAINT pk_game PRIMARY KEY (id),
+ CONSTRAINT fk_game_editor_id FOREIGN KEY (editor_id) REFERENCES editor (id)
+);
+
+CREATE TABLE dlc (
+ id UUID DEFAULT random_uuid() NOT NULL,
+ title VARCHAR NOT NULL,
+ publication_date DATE NOT NULL,
+ price NUMBER NOT NULL DEFAULT 0,
+ game_id UUID NOT NULL,
+ CONSTRAINT pk_dlc PRIMARY KEY (id),
+ CONSTRAINT fk_dlc_game_id FOREIGN KEY (game_id) REFERENCES game (id)
+);
+
+INSERT INTO editor (name, creation_date) VALUES
+('Psyonix', parsedatetime('2000-04-30', 'dd-MM-YYYY'));
\ No newline at end of file