diff --git a/pom.xml b/pom.xml
index 0c754f19..f18a4b0d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,27 +19,57 @@
https://raw.githubusercontent.com/mate-academy/style-guides/master/java/checkstyle.xml
+
+ 3.1.2
+ 3.11.0
+ 1.5.5.Final
+ 0.2.0
+
org.springframework.boot
spring-boot-starter
-
org.springframework.boot
spring-boot-starter-test
test
-
org.springframework.boot
spring-boot-starter-data-jpa
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.projectlombok
+ lombok
+
+
+
+ com.mysql
+ mysql-connector-j
+
com.h2database
h2
+ test
+
+
+
+ org.mapstruct
+ mapstruct
+ ${mapstruct.version}
+
+
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-ui
+ 2.1.0
@@ -48,6 +78,7 @@
org.springframework.boot
spring-boot-maven-plugin
+ ${spring.boot.maven.plugin.version}
org.apache.maven.plugins
@@ -66,9 +97,40 @@
true
true
false
+ src
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven.compiler.plugin.version}
+
+
+ ${java.version}
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+
+
+ org.mapstruct
+ mapstruct-processor
+ ${mapstruct.version}
+
+
+ org.projectlombok
+ lombok-mapstruct-binding
+ ${lombok.mapstruct.binding.version}
+
+
+
+
+ -Amapstruct.unmappedTargetPolicy=IGNORE
+
+
-
diff --git a/src/main/java/mate/academy/rickandmorty/Application.java b/src/main/java/mate/academy/rickandmorty/Application.java
index cdea84fc..581d3b4d 100644
--- a/src/main/java/mate/academy/rickandmorty/Application.java
+++ b/src/main/java/mate/academy/rickandmorty/Application.java
@@ -1,12 +1,36 @@
package mate.academy.rickandmorty;
+import java.util.List;
+import mate.academy.rickandmorty.dto.CharacterResponseDto;
+import mate.academy.rickandmorty.service.CharacterService;
+import mate.academy.rickandmorty.service.RickAndMortyClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class Application {
+ private static RickAndMortyClient rickAndMortyClient;
+ private static CharacterService characterService;
+
+ @Autowired
+ public Application(CharacterService service, RickAndMortyClient client) {
+ Application.rickAndMortyClient = client;
+ Application.characterService = service;
+ }
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
+
+ @Bean
+ public CommandLineRunner commandLineRunnerBean() {
+
+ return (args) -> {
+ List characters = rickAndMortyClient.getCharacters();
+ characterService.saveAll(characters);
+ };
+ }
}
diff --git a/src/main/java/mate/academy/rickandmorty/controller/CharacterController.java b/src/main/java/mate/academy/rickandmorty/controller/CharacterController.java
new file mode 100644
index 00000000..a1670551
--- /dev/null
+++ b/src/main/java/mate/academy/rickandmorty/controller/CharacterController.java
@@ -0,0 +1,33 @@
+package mate.academy.rickandmorty.controller;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import java.util.List;
+import lombok.RequiredArgsConstructor;
+import mate.academy.rickandmorty.dto.CharacterDto;
+import mate.academy.rickandmorty.service.CharacterService;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@Tag(name = "Character management", description = "Endpoints for getting characters from DB")
+@RequiredArgsConstructor
+@RestController
+public class CharacterController {
+ private final CharacterService characterService;
+
+ @Operation(summary = "Get random character",
+ description = "Get a random characters from Rick and Morty world")
+ @GetMapping
+ public CharacterDto getRandomCharacter() {
+ return characterService.getRandomCharacter();
+ }
+
+ @Operation(summary = "Search characters",
+ description = "Search characters by name")
+ @GetMapping("/search")
+ public List searchCharacter(@RequestParam String name) {
+ return characterService.findByName(name);
+ }
+}
+
diff --git a/src/main/java/mate/academy/rickandmorty/dto/CharacterDto.java b/src/main/java/mate/academy/rickandmorty/dto/CharacterDto.java
new file mode 100644
index 00000000..ee4e8282
--- /dev/null
+++ b/src/main/java/mate/academy/rickandmorty/dto/CharacterDto.java
@@ -0,0 +1,12 @@
+package mate.academy.rickandmorty.dto;
+
+import lombok.Data;
+
+@Data
+public class CharacterDto {
+ private Long id;
+ private String externalId;
+ private String name;
+ private String status;
+ private String gender;
+}
diff --git a/src/main/java/mate/academy/rickandmorty/dto/CharacterResponseDto.java b/src/main/java/mate/academy/rickandmorty/dto/CharacterResponseDto.java
new file mode 100644
index 00000000..b6d9856d
--- /dev/null
+++ b/src/main/java/mate/academy/rickandmorty/dto/CharacterResponseDto.java
@@ -0,0 +1,13 @@
+package mate.academy.rickandmorty.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+@Data
+public class CharacterResponseDto {
+ private Long id;
+ private String name;
+ private String status;
+ private String gender;
+}
diff --git a/src/main/java/mate/academy/rickandmorty/dto/ListCharacterDto.java b/src/main/java/mate/academy/rickandmorty/dto/ListCharacterDto.java
new file mode 100644
index 00000000..68a61e4c
--- /dev/null
+++ b/src/main/java/mate/academy/rickandmorty/dto/ListCharacterDto.java
@@ -0,0 +1,9 @@
+package mate.academy.rickandmorty.dto;
+
+import java.util.List;
+import lombok.Data;
+
+@Data
+public class ListCharacterDto {
+ private List results;
+}
diff --git a/src/main/java/mate/academy/rickandmorty/mapper/CharacterMapper.java b/src/main/java/mate/academy/rickandmorty/mapper/CharacterMapper.java
new file mode 100644
index 00000000..8b194ced
--- /dev/null
+++ b/src/main/java/mate/academy/rickandmorty/mapper/CharacterMapper.java
@@ -0,0 +1,16 @@
+package mate.academy.rickandmorty.mapper;
+
+import mate.academy.rickandmorty.dto.CharacterDto;
+import mate.academy.rickandmorty.dto.CharacterResponseDto;
+import mate.academy.rickandmorty.model.Character;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+
+@Mapper(componentModel = "spring")
+public interface CharacterMapper {
+ @Mapping(source = "id", target = "externalId")
+ @Mapping(target = "id", ignore = true)
+ Character toModel(CharacterResponseDto responseDto);
+
+ CharacterDto toDto(Character character);
+}
diff --git a/src/main/java/mate/academy/rickandmorty/model/Character.java b/src/main/java/mate/academy/rickandmorty/model/Character.java
new file mode 100644
index 00000000..bc8e21e9
--- /dev/null
+++ b/src/main/java/mate/academy/rickandmorty/model/Character.java
@@ -0,0 +1,21 @@
+package mate.academy.rickandmorty.model;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+import lombok.Data;
+
+@Data
+@Entity
+@Table(name = "characters")
+public class Character {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+ private String externalId;
+ private String name;
+ private String status;
+ private String gender;
+}
diff --git a/src/main/java/mate/academy/rickandmorty/repository/CharacterRepository.java b/src/main/java/mate/academy/rickandmorty/repository/CharacterRepository.java
new file mode 100644
index 00000000..2e34928f
--- /dev/null
+++ b/src/main/java/mate/academy/rickandmorty/repository/CharacterRepository.java
@@ -0,0 +1,9 @@
+package mate.academy.rickandmorty.repository;
+
+import java.util.List;
+import mate.academy.rickandmorty.model.Character;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface CharacterRepository extends JpaRepository {
+ List findByNameContaining(String name);
+}
diff --git a/src/main/java/mate/academy/rickandmorty/service/CharacterService.java b/src/main/java/mate/academy/rickandmorty/service/CharacterService.java
new file mode 100644
index 00000000..91855b80
--- /dev/null
+++ b/src/main/java/mate/academy/rickandmorty/service/CharacterService.java
@@ -0,0 +1,13 @@
+package mate.academy.rickandmorty.service;
+
+import java.util.List;
+import mate.academy.rickandmorty.dto.CharacterDto;
+import mate.academy.rickandmorty.dto.CharacterResponseDto;
+
+public interface CharacterService {
+ CharacterDto getRandomCharacter();
+
+ void saveAll(List listDto);
+
+ List findByName(String name);
+}
diff --git a/src/main/java/mate/academy/rickandmorty/service/RickAndMortyClient.java b/src/main/java/mate/academy/rickandmorty/service/RickAndMortyClient.java
new file mode 100644
index 00000000..d2a99797
--- /dev/null
+++ b/src/main/java/mate/academy/rickandmorty/service/RickAndMortyClient.java
@@ -0,0 +1,37 @@
+package mate.academy.rickandmorty.service;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.util.List;
+import lombok.RequiredArgsConstructor;
+import mate.academy.rickandmorty.dto.CharacterResponseDto;
+import mate.academy.rickandmorty.dto.ListCharacterDto;
+import org.springframework.stereotype.Component;
+
+@Component
+@RequiredArgsConstructor
+public class RickAndMortyClient {
+ private static final String CHARACTER_URL = "https://rickandmortyapi.com/api/character";
+ private static final HttpClient httpClient = HttpClient.newHttpClient();
+ private final ObjectMapper objectMapper;
+
+ public List getCharacters() {
+ HttpRequest request = HttpRequest.newBuilder()
+ .GET()
+ .uri(URI.create(CHARACTER_URL))
+ .build();
+ try {
+ HttpResponse response = httpClient
+ .send(request, HttpResponse.BodyHandlers.ofString());
+ ListCharacterDto listCharacterDto = objectMapper
+ .readValue(response.body(), ListCharacterDto.class);
+ return listCharacterDto.getResults();
+ } catch (IOException | InterruptedException e) {
+ throw new RuntimeException("Can't get list of characters by request", e);
+ }
+ }
+}
diff --git a/src/main/java/mate/academy/rickandmorty/service/impl/CharacterServiceImpl.java b/src/main/java/mate/academy/rickandmorty/service/impl/CharacterServiceImpl.java
new file mode 100644
index 00000000..0d69b454
--- /dev/null
+++ b/src/main/java/mate/academy/rickandmorty/service/impl/CharacterServiceImpl.java
@@ -0,0 +1,44 @@
+package mate.academy.rickandmorty.service.impl;
+
+import java.util.List;
+import lombok.RequiredArgsConstructor;
+import mate.academy.rickandmorty.dto.CharacterDto;
+import mate.academy.rickandmorty.dto.CharacterResponseDto;
+import mate.academy.rickandmorty.mapper.CharacterMapper;
+import mate.academy.rickandmorty.model.Character;
+import mate.academy.rickandmorty.repository.CharacterRepository;
+import mate.academy.rickandmorty.service.CharacterService;
+import org.springframework.stereotype.Service;
+
+@RequiredArgsConstructor
+@Service
+public class CharacterServiceImpl implements CharacterService {
+ private int listSize;
+ private final RandomUtil random;
+ private final CharacterRepository characterRepository;
+ private final CharacterMapper mapper;
+
+ @Override
+ public CharacterDto getRandomCharacter() {
+ int id = random.util().nextInt(listSize) + 1;
+ return mapper.toDto(characterRepository.findById((long) id).orElseThrow(
+ () -> new RuntimeException("Characters not found by id: " + id))
+ );
+ }
+
+ @Override
+ public void saveAll(List listDto) {
+ List characters = listDto.stream()
+ .map(mapper::toModel)
+ .toList();
+ listSize = characters.size();
+ characterRepository.saveAll(characters);
+ }
+
+ @Override
+ public List findByName(String name) {
+ return characterRepository.findByNameContaining(name).stream()
+ .map(mapper::toDto)
+ .toList();
+ }
+}
diff --git a/src/main/java/mate/academy/rickandmorty/service/impl/RandomUtil.java b/src/main/java/mate/academy/rickandmorty/service/impl/RandomUtil.java
new file mode 100644
index 00000000..60f05ea3
--- /dev/null
+++ b/src/main/java/mate/academy/rickandmorty/service/impl/RandomUtil.java
@@ -0,0 +1,13 @@
+package mate.academy.rickandmorty.service.impl;
+
+import java.util.Random;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class RandomUtil {
+ @Bean
+ Random util() {
+ return new Random();
+ }
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 8b137891..ce8f2591 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1 +1,9 @@
+spring.datasource.url=jdbc:mysql://localhost:3306/rick_and_morty?serverTimezone=UTC
+spring.datasource.username=root
+spring.datasource.password=Romaxa051979
+spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
+spring.jpa.hibernate.ddl-auto=create-drop
+server.servlet.context-path=/api
+spring.jpa.show-sql=true
+spring.jpa.open-in-view=false
diff --git a/src/test/java/mate/academy/rickandmorty/ApplicationTests.java b/src/test/java/mate/academy/rickandmorty/ApplicationTests.java
index 8fec6af0..4fe47c0d 100644
--- a/src/test/java/mate/academy/rickandmorty/ApplicationTests.java
+++ b/src/test/java/mate/academy/rickandmorty/ApplicationTests.java
@@ -5,9 +5,7 @@
@SpringBootTest
class ApplicationTests {
-
- @Test
- void contextLoads() {
- }
-
+ @Test
+ void contextLoads() {
+ }
}