From 291a4d1663229b0d46f6bbad5bab720320e45cf0 Mon Sep 17 00:00:00 2001 From: Dmytro Martyshchuk Date: Wed, 18 Oct 2023 21:05:40 +0300 Subject: [PATCH 1/3] Add Character Model, Repository, Service, Mapper, Controller --- pom.xml | 54 ++++++++++++++++++- .../rickandmorty/config/MapperConfig.java | 13 +++++ .../controller/CharacterController.java | 37 +++++++++++++ .../dto/CharacterResponseDto.java | 10 ++++ .../rickandmorty/mapper/CharacterMapper.java | 11 ++++ .../academy/rickandmorty/model/Character.java | 22 ++++++++ .../repository/CharacterRepository.java | 11 ++++ .../service/CharacterService.java | 9 ++++ .../service/impl/CharacterServiceImpl.java | 31 +++++++++++ src/main/resources/application.properties | 8 +++ src/test/resources/application.properties | 5 ++ 11 files changed, 209 insertions(+), 2 deletions(-) create mode 100644 src/main/java/mate/academy/rickandmorty/config/MapperConfig.java create mode 100644 src/main/java/mate/academy/rickandmorty/controller/CharacterController.java create mode 100644 src/main/java/mate/academy/rickandmorty/dto/CharacterResponseDto.java create mode 100644 src/main/java/mate/academy/rickandmorty/mapper/CharacterMapper.java create mode 100644 src/main/java/mate/academy/rickandmorty/model/Character.java create mode 100644 src/main/java/mate/academy/rickandmorty/repository/CharacterRepository.java create mode 100644 src/main/java/mate/academy/rickandmorty/service/CharacterService.java create mode 100644 src/main/java/mate/academy/rickandmorty/service/impl/CharacterServiceImpl.java diff --git a/pom.xml b/pom.xml index 0c754f19..803906a4 100644 --- a/pom.xml +++ b/pom.xml @@ -19,13 +19,34 @@ https://raw.githubusercontent.com/mate-academy/style-guides/master/java/checkstyle.xml + 8.0.33 + 2.0.3 + 1.5.5.Final + 0.2.0 org.springframework.boot spring-boot-starter - + + org.springframework.boot + spring-boot-starter-web + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + ${openapi.version} + + + org.projectlombok + lombok + + + org.mapstruct + mapstruct + ${mapstruct.version} + org.springframework.boot spring-boot-starter-test @@ -36,7 +57,11 @@ org.springframework.boot spring-boot-starter-data-jpa - + + mysql + mysql-connector-java + ${mysql.version} + com.h2database h2 @@ -68,6 +93,31 @@ false + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.version} + ${java.version} + + + org.projectlombok + lombok + ${lombok.version} + + + org.projectlombok + lombok-mapstruct-binding + ${lombok.mapstruct.binding.version} + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + + + diff --git a/src/main/java/mate/academy/rickandmorty/config/MapperConfig.java b/src/main/java/mate/academy/rickandmorty/config/MapperConfig.java new file mode 100644 index 00000000..cbea0738 --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/config/MapperConfig.java @@ -0,0 +1,13 @@ +package mate.academy.rickandmorty.config; + +import org.mapstruct.InjectionStrategy; +import org.mapstruct.NullValueCheckStrategy; + +@org.mapstruct.MapperConfig( + componentModel = "spring", + injectionStrategy = InjectionStrategy.CONSTRUCTOR, + nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, + implementationName = ".impl" +) +public class MapperConfig { +} 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..ad2a4bc8 --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/controller/CharacterController.java @@ -0,0 +1,37 @@ +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 mate.academy.rickandmorty.dto.CharacterResponseDto; +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; + +@Tag(name = "Character API", description = "Look at and search for characters") +@RestController +@RequestMapping(value = "/characters") +public class CharacterController { + + @Operation( + summary = "Get random character", + description = "The request randomly generates a wiki about one" + + " character in the universe the animated series Rick & Morty" + ) + @GetMapping("/random") + public CharacterResponseDto getRandom() { + return null; + } + + @Operation( + summary = "Search character", + description = "Search for characters based on the name" + ) + @GetMapping("/search") + public List search(@PathVariable String name) { + return List.of(); + } + + +} 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..43be6bc0 --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/dto/CharacterResponseDto.java @@ -0,0 +1,10 @@ +package mate.academy.rickandmorty.dto; + +public record CharacterResponseDto( + Long id, + Long externalId, + String name, + String status, + String gender +) { +} 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..998919ea --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/mapper/CharacterMapper.java @@ -0,0 +1,11 @@ +package mate.academy.rickandmorty.mapper; + +import mate.academy.rickandmorty.config.MapperConfig; +import mate.academy.rickandmorty.dto.CharacterResponseDto; +import mate.academy.rickandmorty.model.Character; +import org.mapstruct.Mapper; + +@Mapper(config = MapperConfig.class) +public interface CharacterMapper { + CharacterResponseDto 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..48480be9 --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/model/Character.java @@ -0,0 +1,22 @@ +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.AUTO) + private Long id; + private Long 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..43ffc416 --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/repository/CharacterRepository.java @@ -0,0 +1,11 @@ +package mate.academy.rickandmorty.repository; + +import java.util.List; +import mate.academy.rickandmorty.model.Character; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface CharacterRepository extends JpaRepository { + List findCharacterByNameLike(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..6af6f352 --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/service/CharacterService.java @@ -0,0 +1,9 @@ +package mate.academy.rickandmorty.service; + +import java.util.List; +import mate.academy.rickandmorty.dto.CharacterResponseDto; + +public interface CharacterService { + CharacterResponseDto getRandom(); + List search(String name); +} 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..d2767c76 --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/service/impl/CharacterServiceImpl.java @@ -0,0 +1,31 @@ +package mate.academy.rickandmorty.service.impl; + +import java.util.List; +import lombok.RequiredArgsConstructor; +import mate.academy.rickandmorty.dto.CharacterResponseDto; +import mate.academy.rickandmorty.mapper.CharacterMapper; +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 final CharacterRepository characterRepository; + private final CharacterMapper characterMapper; + + + @Override + public CharacterResponseDto getRandom() { + return characterRepository.findById(1L) + .map(characterMapper::toDto) + .orElseThrow(); + } + + @Override + public List search(String name) { + return characterRepository.findCharacterByNameLike(name).stream() + .map(characterMapper::toDto) + .toList(); + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 8b137891..81174ed8 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 +spring.datasource.username=test +spring.datasource.password=StrongSecret1234 +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver +spring.jpa.hibernate.ddl-auto=create-drop +spring.jpa.open-in-view=false + +server.servlet.context-path=api diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties index bc2fdde8..4cc7e08e 100644 --- a/src/test/resources/application.properties +++ b/src/test/resources/application.properties @@ -3,3 +3,8 @@ spring.datasource.driverClassName=org.h2.Driver spring.datasource.username=sa spring.datasource.password=password spring.jpa.database-platform=org.hibernate.dialect.H2Dialect + +spring.jpa.hibernate.ddl-auto=create-drop +spring.jpa.open-in-view=false + +server.servlet.context-path=api From f3695de4dac416f31fdd0902282970be2f4dd38e Mon Sep 17 00:00:00 2001 From: Dmytro Martyshchuk Date: Thu, 19 Oct 2023 12:36:55 +0300 Subject: [PATCH 2/3] Add support for Rick&Morty public API --- pom.xml | 1 + .../rickandmorty/config/DataFetchConfig.java | 61 +++++++++++++++++++ .../rickandmorty/config/MapperConfig.java | 2 +- .../controller/CharacterController.java | 14 +++-- .../external/CharacterResponseDataDto.java | 9 +++ .../dto/external/ExternalCharacterDto.java | 4 ++ .../external/ExternalCharacterInfoDto.java | 4 ++ .../rickandmorty/mapper/CharacterMapper.java | 6 ++ .../repository/CharacterRepository.java | 2 +- .../service/CharacterService.java | 5 ++ .../service/impl/CharacterServiceImpl.java | 23 ++++++- src/main/resources/application.properties | 4 +- .../rickandmorty/ApplicationTests.java | 8 +-- src/test/resources/application.properties | 4 +- 14 files changed, 129 insertions(+), 18 deletions(-) create mode 100644 src/main/java/mate/academy/rickandmorty/config/DataFetchConfig.java create mode 100644 src/main/java/mate/academy/rickandmorty/dto/external/CharacterResponseDataDto.java create mode 100644 src/main/java/mate/academy/rickandmorty/dto/external/ExternalCharacterDto.java create mode 100644 src/main/java/mate/academy/rickandmorty/dto/external/ExternalCharacterInfoDto.java diff --git a/pom.xml b/pom.xml index 803906a4..0ab158aa 100644 --- a/pom.xml +++ b/pom.xml @@ -91,6 +91,7 @@ true true false + src diff --git a/src/main/java/mate/academy/rickandmorty/config/DataFetchConfig.java b/src/main/java/mate/academy/rickandmorty/config/DataFetchConfig.java new file mode 100644 index 00000000..ecf8731a --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/config/DataFetchConfig.java @@ -0,0 +1,61 @@ +package mate.academy.rickandmorty.config; + +import com.fasterxml.jackson.core.type.TypeReference; +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.ArrayList; +import java.util.List; +import lombok.RequiredArgsConstructor; +import mate.academy.rickandmorty.dto.external.CharacterResponseDataDto; +import mate.academy.rickandmorty.dto.external.ExternalCharacterDto; +import mate.academy.rickandmorty.service.CharacterService; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.event.EventListener; + +@RequiredArgsConstructor +@Configuration +public class DataFetchConfig { + private final CharacterService service; + private final ObjectMapper objectMapper; + @Value("${rickandmorty.api.url}") + private String characterUrl; + + @EventListener(ApplicationReadyEvent.class) + public void fetchData() { + List characters = new ArrayList<>(); + CharacterResponseDataDto response = loadData(characterUrl); + do { + characters.addAll(response.results()); + response = loadData(response.info().next()); + } while (response.info().next() != null); + service.saveAll(characters); + } + + private CharacterResponseDataDto loadData(String url) { + HttpClient httpClient = HttpClient.newHttpClient(); + + HttpRequest request = HttpRequest.newBuilder() + .GET() + .uri(URI.create(url)) + .build(); + + try { + HttpResponse response = httpClient.send( + request, + HttpResponse.BodyHandlers.ofByteArray() + ); + return objectMapper.readValue( + response.body(), new TypeReference<>() { + } + ); + } catch (IOException | InterruptedException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/mate/academy/rickandmorty/config/MapperConfig.java b/src/main/java/mate/academy/rickandmorty/config/MapperConfig.java index cbea0738..450e58db 100644 --- a/src/main/java/mate/academy/rickandmorty/config/MapperConfig.java +++ b/src/main/java/mate/academy/rickandmorty/config/MapperConfig.java @@ -7,7 +7,7 @@ componentModel = "spring", injectionStrategy = InjectionStrategy.CONSTRUCTOR, nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, - implementationName = ".impl" + implementationPackage = ".impl" ) public class MapperConfig { } diff --git a/src/main/java/mate/academy/rickandmorty/controller/CharacterController.java b/src/main/java/mate/academy/rickandmorty/controller/CharacterController.java index ad2a4bc8..e762615a 100644 --- a/src/main/java/mate/academy/rickandmorty/controller/CharacterController.java +++ b/src/main/java/mate/academy/rickandmorty/controller/CharacterController.java @@ -3,16 +3,20 @@ 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.CharacterResponseDto; +import mate.academy.rickandmorty.service.CharacterService; 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.RequestParam; import org.springframework.web.bind.annotation.RestController; @Tag(name = "Character API", description = "Look at and search for characters") +@RequiredArgsConstructor @RestController @RequestMapping(value = "/characters") public class CharacterController { + private final CharacterService characterService; @Operation( summary = "Get random character", @@ -21,7 +25,7 @@ public class CharacterController { ) @GetMapping("/random") public CharacterResponseDto getRandom() { - return null; + return characterService.getRandom(); } @Operation( @@ -29,9 +33,7 @@ public CharacterResponseDto getRandom() { description = "Search for characters based on the name" ) @GetMapping("/search") - public List search(@PathVariable String name) { - return List.of(); + public List search(@RequestParam String name) { + return characterService.search(name); } - - } diff --git a/src/main/java/mate/academy/rickandmorty/dto/external/CharacterResponseDataDto.java b/src/main/java/mate/academy/rickandmorty/dto/external/CharacterResponseDataDto.java new file mode 100644 index 00000000..6fcc23ac --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/dto/external/CharacterResponseDataDto.java @@ -0,0 +1,9 @@ +package mate.academy.rickandmorty.dto.external; + +import java.util.List; + +public record CharacterResponseDataDto( + ExternalCharacterInfoDto info, + List results +) { +} diff --git a/src/main/java/mate/academy/rickandmorty/dto/external/ExternalCharacterDto.java b/src/main/java/mate/academy/rickandmorty/dto/external/ExternalCharacterDto.java new file mode 100644 index 00000000..a6fb1c1e --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/dto/external/ExternalCharacterDto.java @@ -0,0 +1,4 @@ +package mate.academy.rickandmorty.dto.external; + +public record ExternalCharacterDto(Long id, String name, String status, String gender) { +} diff --git a/src/main/java/mate/academy/rickandmorty/dto/external/ExternalCharacterInfoDto.java b/src/main/java/mate/academy/rickandmorty/dto/external/ExternalCharacterInfoDto.java new file mode 100644 index 00000000..25de7d87 --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/dto/external/ExternalCharacterInfoDto.java @@ -0,0 +1,4 @@ +package mate.academy.rickandmorty.dto.external; + +public record ExternalCharacterInfoDto(Long count, Long pages, String next, String prev) { +} diff --git a/src/main/java/mate/academy/rickandmorty/mapper/CharacterMapper.java b/src/main/java/mate/academy/rickandmorty/mapper/CharacterMapper.java index 998919ea..5215f75f 100644 --- a/src/main/java/mate/academy/rickandmorty/mapper/CharacterMapper.java +++ b/src/main/java/mate/academy/rickandmorty/mapper/CharacterMapper.java @@ -2,10 +2,16 @@ import mate.academy.rickandmorty.config.MapperConfig; import mate.academy.rickandmorty.dto.CharacterResponseDto; +import mate.academy.rickandmorty.dto.external.ExternalCharacterDto; import mate.academy.rickandmorty.model.Character; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; @Mapper(config = MapperConfig.class) public interface CharacterMapper { CharacterResponseDto toDto(Character character); + + @Mapping(target = "externalId", source = "id") + @Mapping(target = "id", ignore = true) + Character toModel(ExternalCharacterDto characterDto); } diff --git a/src/main/java/mate/academy/rickandmorty/repository/CharacterRepository.java b/src/main/java/mate/academy/rickandmorty/repository/CharacterRepository.java index 43ffc416..be3f14ab 100644 --- a/src/main/java/mate/academy/rickandmorty/repository/CharacterRepository.java +++ b/src/main/java/mate/academy/rickandmorty/repository/CharacterRepository.java @@ -7,5 +7,5 @@ @Repository public interface CharacterRepository extends JpaRepository { - List findCharacterByNameLike(String name); + List findCharacterByNameContaining(String name); } diff --git a/src/main/java/mate/academy/rickandmorty/service/CharacterService.java b/src/main/java/mate/academy/rickandmorty/service/CharacterService.java index 6af6f352..f2d4b996 100644 --- a/src/main/java/mate/academy/rickandmorty/service/CharacterService.java +++ b/src/main/java/mate/academy/rickandmorty/service/CharacterService.java @@ -2,8 +2,13 @@ import java.util.List; import mate.academy.rickandmorty.dto.CharacterResponseDto; +import mate.academy.rickandmorty.dto.external.ExternalCharacterDto; +import mate.academy.rickandmorty.model.Character; public interface CharacterService { CharacterResponseDto getRandom(); + List search(String name); + + List saveAll(List externalCharacterDtos); } diff --git a/src/main/java/mate/academy/rickandmorty/service/impl/CharacterServiceImpl.java b/src/main/java/mate/academy/rickandmorty/service/impl/CharacterServiceImpl.java index d2767c76..bb2a22f6 100644 --- a/src/main/java/mate/academy/rickandmorty/service/impl/CharacterServiceImpl.java +++ b/src/main/java/mate/academy/rickandmorty/service/impl/CharacterServiceImpl.java @@ -1,31 +1,48 @@ package mate.academy.rickandmorty.service.impl; import java.util.List; +import java.util.Random; import lombok.RequiredArgsConstructor; import mate.academy.rickandmorty.dto.CharacterResponseDto; +import mate.academy.rickandmorty.dto.external.ExternalCharacterDto; 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; +import org.springframework.transaction.annotation.Transactional; @RequiredArgsConstructor @Service public class CharacterServiceImpl implements CharacterService { + private static final long minId = 1L; private final CharacterRepository characterRepository; private final CharacterMapper characterMapper; - @Override public CharacterResponseDto getRandom() { - return characterRepository.findById(1L) + long numberOfCharacters = characterRepository.count(); + Random random = new Random(); + long randomId = random.nextLong(numberOfCharacters + minId); + return characterRepository.findById(randomId) .map(characterMapper::toDto) .orElseThrow(); } @Override public List search(String name) { - return characterRepository.findCharacterByNameLike(name).stream() + return characterRepository.findCharacterByNameContaining(name) + .stream() .map(characterMapper::toDto) .toList(); } + + @Transactional + @Override + public List saveAll(List externalCharacterDtos) { + List characters = externalCharacterDtos.stream() + .map(characterMapper::toModel) + .toList(); + return characterRepository.saveAll(characters); + } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 81174ed8..9d068558 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -6,4 +6,6 @@ spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.jpa.hibernate.ddl-auto=create-drop spring.jpa.open-in-view=false -server.servlet.context-path=api +server.servlet.context-path=/api + +rickandmorty.api.url=https://rickandmortyapi.com/api/character 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() { + } } diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties index 4cc7e08e..7734629c 100644 --- a/src/test/resources/application.properties +++ b/src/test/resources/application.properties @@ -7,4 +7,6 @@ spring.jpa.database-platform=org.hibernate.dialect.H2Dialect spring.jpa.hibernate.ddl-auto=create-drop spring.jpa.open-in-view=false -server.servlet.context-path=api +server.servlet.context-path=/api + +rickandmorty.api.url=https://rickandmortyapi.com/api/character From 3a0ab9970603807051fd3eb82d6a57a56753da89 Mon Sep 17 00:00:00 2001 From: Dmytro Martyshchuk Date: Thu, 19 Oct 2023 16:54:35 +0300 Subject: [PATCH 3/3] Replace HttpClient with FeinClient --- pom.xml | 14 +++++ .../academy/rickandmorty/Application.java | 2 + .../client/RickAndMortyClient.java | 14 +++++ .../rickandmorty/config/DataFetchConfig.java | 52 +++++-------------- src/main/resources/application.properties | 2 +- src/test/resources/application.properties | 2 +- 6 files changed, 45 insertions(+), 41 deletions(-) create mode 100644 src/main/java/mate/academy/rickandmorty/client/RickAndMortyClient.java diff --git a/pom.xml b/pom.xml index 0ab158aa..45b0cbb3 100644 --- a/pom.xml +++ b/pom.xml @@ -22,6 +22,8 @@ 8.0.33 2.0.3 1.5.5.Final + 4.0.4 + 2022.0.4 0.2.0 @@ -29,6 +31,18 @@ org.springframework.boot spring-boot-starter + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + org.springframework.cloud + spring-cloud-starter-openfeign + ${openfein.version} + org.springframework.boot spring-boot-starter-web diff --git a/src/main/java/mate/academy/rickandmorty/Application.java b/src/main/java/mate/academy/rickandmorty/Application.java index cdea84fc..c66e514c 100644 --- a/src/main/java/mate/academy/rickandmorty/Application.java +++ b/src/main/java/mate/academy/rickandmorty/Application.java @@ -2,8 +2,10 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication +@EnableFeignClients public class Application { public static void main(String[] args) { diff --git a/src/main/java/mate/academy/rickandmorty/client/RickAndMortyClient.java b/src/main/java/mate/academy/rickandmorty/client/RickAndMortyClient.java new file mode 100644 index 00000000..992b7cfa --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/client/RickAndMortyClient.java @@ -0,0 +1,14 @@ +package mate.academy.rickandmorty.client; + +import mate.academy.rickandmorty.dto.external.CharacterResponseDataDto; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@FeignClient(value = "characters", url = "${rickandmorty.api.url}") +public interface RickAndMortyClient { + + @RequestMapping(method = RequestMethod.GET, value = "/character?page={page}") + CharacterResponseDataDto getCharactersData(@PathVariable Long page); +} diff --git a/src/main/java/mate/academy/rickandmorty/config/DataFetchConfig.java b/src/main/java/mate/academy/rickandmorty/config/DataFetchConfig.java index ecf8731a..b871826d 100644 --- a/src/main/java/mate/academy/rickandmorty/config/DataFetchConfig.java +++ b/src/main/java/mate/academy/rickandmorty/config/DataFetchConfig.java @@ -1,19 +1,12 @@ package mate.academy.rickandmorty.config; -import com.fasterxml.jackson.core.type.TypeReference; -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.ArrayList; import java.util.List; import lombok.RequiredArgsConstructor; +import mate.academy.rickandmorty.client.RickAndMortyClient; import mate.academy.rickandmorty.dto.external.CharacterResponseDataDto; import mate.academy.rickandmorty.dto.external.ExternalCharacterDto; import mate.academy.rickandmorty.service.CharacterService; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.annotation.Configuration; import org.springframework.context.event.EventListener; @@ -21,41 +14,22 @@ @RequiredArgsConstructor @Configuration public class DataFetchConfig { - private final CharacterService service; - private final ObjectMapper objectMapper; - @Value("${rickandmorty.api.url}") - private String characterUrl; + private static final Long firstPage = 1L; + private final RickAndMortyClient rickAndMortyClient; + private final CharacterService characterService; @EventListener(ApplicationReadyEvent.class) public void fetchData() { - List characters = new ArrayList<>(); - CharacterResponseDataDto response = loadData(characterUrl); - do { - characters.addAll(response.results()); - response = loadData(response.info().next()); - } while (response.info().next() != null); - service.saveAll(characters); - } + List externalCharacterDtos = new ArrayList<>(); - private CharacterResponseDataDto loadData(String url) { - HttpClient httpClient = HttpClient.newHttpClient(); - - HttpRequest request = HttpRequest.newBuilder() - .GET() - .uri(URI.create(url)) - .build(); + long currentPage = firstPage; + CharacterResponseDataDto charactersData; + do { + charactersData = rickAndMortyClient.getCharactersData(currentPage); + externalCharacterDtos.addAll(charactersData.results()); + currentPage++; + } while (currentPage <= charactersData.info().pages()); - try { - HttpResponse response = httpClient.send( - request, - HttpResponse.BodyHandlers.ofByteArray() - ); - return objectMapper.readValue( - response.body(), new TypeReference<>() { - } - ); - } catch (IOException | InterruptedException e) { - throw new RuntimeException(e); - } + characterService.saveAll(externalCharacterDtos); } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 9d068558..a47af3ae 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -8,4 +8,4 @@ spring.jpa.open-in-view=false server.servlet.context-path=/api -rickandmorty.api.url=https://rickandmortyapi.com/api/character +rickandmorty.api.url=https://rickandmortyapi.com/api diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties index 7734629c..5bc88f08 100644 --- a/src/test/resources/application.properties +++ b/src/test/resources/application.properties @@ -9,4 +9,4 @@ spring.jpa.open-in-view=false server.servlet.context-path=/api -rickandmorty.api.url=https://rickandmortyapi.com/api/character +rickandmorty.api.url=https://rickandmortyapi.com/api