From ceb3a62acc4193cf020a19d53a7fa1324d81a91d Mon Sep 17 00:00:00 2001 From: Watxesnavo Date: Tue, 24 Oct 2023 14:04:22 +0200 Subject: [PATCH 1/7] task done, to review added: client connection get random character info get a character with string occurrence in the name --- pom.xml | 83 +++++++++++++++++++ .../rickandmorty/config/MapperConfig.java | 13 +++ .../controller/CharacterController.java | 53 ++++++++++++ .../dto/external/ExternalCharResponseDto.java | 8 ++ .../rickandmorty/dto/internal/InfoDto.java | 5 ++ .../dto/internal/InternalCharListDto.java | 11 +++ .../dto/internal/InternalCharResponseDto.java | 7 ++ .../rickandmorty/mapper/CharacterMapper.java | 29 +++++++ .../academy/rickandmorty/model/Character.java | 23 +++++ .../repository/CharacterRepository.java | 10 +++ .../service/CharacterService.java | 10 +++ .../service/RickAndMortyClient.java | 80 ++++++++++++++++++ .../service/impl/CharacterServiceImpl.java | 50 +++++++++++ src/main/resources/application.properties | 7 ++ .../changes/01-create-characters-table.yaml | 36 ++++++++ .../db/changelog/db.changelog-master.yaml | 3 + .../rickandmorty/ApplicationTests.java | 8 +- 17 files changed, 431 insertions(+), 5 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/external/ExternalCharResponseDto.java create mode 100644 src/main/java/mate/academy/rickandmorty/dto/internal/InfoDto.java create mode 100644 src/main/java/mate/academy/rickandmorty/dto/internal/InternalCharListDto.java create mode 100644 src/main/java/mate/academy/rickandmorty/dto/internal/InternalCharResponseDto.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/RickAndMortyClient.java create mode 100644 src/main/java/mate/academy/rickandmorty/service/impl/CharacterServiceImpl.java create mode 100644 src/main/resources/db/changelog/changes/01-create-characters-table.yaml create mode 100644 src/main/resources/db/changelog/db.changelog-master.yaml diff --git a/pom.xml b/pom.xml index 0c754f19..ab69d86a 100644 --- a/pom.xml +++ b/pom.xml @@ -19,6 +19,10 @@ https://raw.githubusercontent.com/mate-academy/style-guides/master/java/checkstyle.xml + 1.5.5.Final + 17 + 17 + 0.2.0 @@ -32,11 +36,58 @@ test + + org.springframework.boot + spring-boot-starter-web + + org.springframework.boot spring-boot-starter-data-jpa + + org.springdoc + springdoc-openapi-starter-webmvc-ui + 2.1.0 + + + + org.projectlombok + lombok + 1.18.28 + + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + + + org.mapstruct + mapstruct + ${mapstruct.version} + + + + org.liquibase + liquibase-core + ${liquibase.version} + + + + org.liquibase + liquibase-maven-plugin + ${liquibase.version} + + + + mysql + mysql-connector-java + 8.0.32 + + com.h2database h2 @@ -63,12 +114,44 @@ ${maven.checkstyle.plugin.configLocation} + src true true false + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + ${maven.compiler.source} + ${maven.compiler.target} + ${project.build.sourceEncoding} + + + 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..450e58db --- /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, + 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 new file mode 100644 index 00000000..d140a179 --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/controller/CharacterController.java @@ -0,0 +1,53 @@ +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.external.ExternalCharResponseDto; +import mate.academy.rickandmorty.mapper.CharacterMapper; +import mate.academy.rickandmorty.model.Character; +import mate.academy.rickandmorty.service.CharacterService; +import mate.academy.rickandmorty.service.RickAndMortyClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@Tag(name = "Character Management", description = "endpoints to manage client character data base") +@RestController +@RequestMapping("/characters") +@RequiredArgsConstructor +public class CharacterController { + private final RickAndMortyClient client; + private final CharacterService service; + private final CharacterMapper mapper; + + @Operation(summary = "show random character", + description = "show and save random character from client db") + @GetMapping("/random") + public ExternalCharResponseDto getRandomCharacter() { + return client.getRandomCharacter(); + } + + @Operation(summary = "saving all characters from client to db", + description = "save all char info from client db to yours," + + " only to use once for the first time with empty table") + @GetMapping("/save") + public void save() { + service.save(); + } + + @Operation(summary = "show character by search name parameter", + description = "show character by string occurrence in his/her/its name") + @GetMapping + public List getCharactersBySearchString( + @RequestParam String searchString + ) { + List charactersBySearchString = + service.getCharactersBySearchString(searchString); + return charactersBySearchString.stream() + .map(mapper::toDto) + .toList(); + } +} diff --git a/src/main/java/mate/academy/rickandmorty/dto/external/ExternalCharResponseDto.java b/src/main/java/mate/academy/rickandmorty/dto/external/ExternalCharResponseDto.java new file mode 100644 index 00000000..7c3108d0 --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/dto/external/ExternalCharResponseDto.java @@ -0,0 +1,8 @@ +package mate.academy.rickandmorty.dto.external; + +public record ExternalCharResponseDto(Long id, + Long externalId, + String name, + String status, + String gender) { +} diff --git a/src/main/java/mate/academy/rickandmorty/dto/internal/InfoDto.java b/src/main/java/mate/academy/rickandmorty/dto/internal/InfoDto.java new file mode 100644 index 00000000..85acdeb2 --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/dto/internal/InfoDto.java @@ -0,0 +1,5 @@ +package mate.academy.rickandmorty.dto.internal; + +public record InfoDto(Long count, + Long pages) { +} diff --git a/src/main/java/mate/academy/rickandmorty/dto/internal/InternalCharListDto.java b/src/main/java/mate/academy/rickandmorty/dto/internal/InternalCharListDto.java new file mode 100644 index 00000000..0436b27a --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/dto/internal/InternalCharListDto.java @@ -0,0 +1,11 @@ +package mate.academy.rickandmorty.dto.internal; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import lombok.Data; + +@Data +public class InternalCharListDto { + @JsonProperty("results") + private List results; +} diff --git a/src/main/java/mate/academy/rickandmorty/dto/internal/InternalCharResponseDto.java b/src/main/java/mate/academy/rickandmorty/dto/internal/InternalCharResponseDto.java new file mode 100644 index 00000000..c58fc778 --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/dto/internal/InternalCharResponseDto.java @@ -0,0 +1,7 @@ +package mate.academy.rickandmorty.dto.internal; + +public record InternalCharResponseDto(Long id, + 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..0fd4fd28 --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/mapper/CharacterMapper.java @@ -0,0 +1,29 @@ +package mate.academy.rickandmorty.mapper; + +import mate.academy.rickandmorty.config.MapperConfig; +import mate.academy.rickandmorty.dto.external.ExternalCharResponseDto; +import mate.academy.rickandmorty.dto.internal.InternalCharResponseDto; +import mate.academy.rickandmorty.model.Character; +import org.mapstruct.Mapper; + +@Mapper(config = MapperConfig.class) +public interface CharacterMapper { + default Character toEntity(InternalCharResponseDto responseDto) { + Character character = new Character(); + character.setExternalId(responseDto.id()); + character.setName(responseDto.name()); + character.setStatus(responseDto.status()); + character.setGender(responseDto.gender()); + return character; + } + + default ExternalCharResponseDto toDto(Character character) { + return new ExternalCharResponseDto( + character.getId(), + character.getExternalId(), + character.getName(), + character.getStatus(), + character.getGender() + ); + } +} 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..b49eb4f1 --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/model/Character.java @@ -0,0 +1,23 @@ +package mate.academy.rickandmorty.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Data; + +@Entity +@Table(name = "characters") +@Data +public class Character { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @JsonProperty("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..7647f734 --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/repository/CharacterRepository.java @@ -0,0 +1,10 @@ +package mate.academy.rickandmorty.repository; + +import mate.academy.rickandmorty.model.Character; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +public interface CharacterRepository extends JpaRepository { + @Query(value = "SELECT * FROM characters c WHERE c.external_id = :id", nativeQuery = true) + Character findByExternalId(Long id); +} 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..479b051a --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/service/CharacterService.java @@ -0,0 +1,10 @@ +package mate.academy.rickandmorty.service; + +import java.util.List; +import mate.academy.rickandmorty.model.Character; + +public interface CharacterService { + void save(); + + List getCharactersBySearchString(String searchString); +} 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..9689eed9 --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/service/RickAndMortyClient.java @@ -0,0 +1,80 @@ +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.Random; +import lombok.RequiredArgsConstructor; +import mate.academy.rickandmorty.dto.external.ExternalCharResponseDto; +import mate.academy.rickandmorty.dto.internal.InternalCharListDto; +import mate.academy.rickandmorty.dto.internal.InternalCharResponseDto; +import mate.academy.rickandmorty.mapper.CharacterMapper; +import mate.academy.rickandmorty.model.Character; +import mate.academy.rickandmorty.repository.CharacterRepository; +import org.springframework.stereotype.Component; +import org.springframework.web.util.UriComponentsBuilder; + +@Component +@RequiredArgsConstructor +public class RickAndMortyClient { + private static final String GET_CHAR_BASE_URL = "https://rickandmortyapi.com/api/character/%s"; + private static final String GET_ALL_BASE_URL = "https://rickandmortyapi.com/api/character"; + private final CharacterMapper characterMapper; + private final ObjectMapper objectMapper; + private final CharacterRepository characterRepository; + + public ExternalCharResponseDto getRandomCharacter() { + Random random = new Random(); + int maxCharAmount = 826; + HttpClient httpClient = HttpClient.newHttpClient(); + String url = GET_CHAR_BASE_URL.formatted(String.valueOf(random.nextInt(maxCharAmount))); + HttpRequest httpRequest = HttpRequest.newBuilder() + .GET() + .uri(URI.create(url)) + .build(); + try { + HttpResponse response = httpClient + .send(httpRequest, HttpResponse.BodyHandlers.ofString()); + InternalCharResponseDto internalCharResponseDto = objectMapper.readValue( + response.body(), InternalCharResponseDto.class + ); + if (!characterRepository.findAll().isEmpty()) { + for (Character c : characterRepository.findAll()) { + if (c.getExternalId().equals(internalCharResponseDto.id())) { + return characterMapper.toDto(c); + } + } + } + Character saved = characterRepository + .save(characterMapper.toEntity(internalCharResponseDto)); + return characterMapper.toDto(saved); + } catch (IOException | InterruptedException e) { + throw new RuntimeException(e); + } + } + + public InternalCharListDto getAllCharacters(Integer page) { + HttpClient httpClient = HttpClient.newHttpClient(); + String url = UriComponentsBuilder + .fromHttpUrl(GET_ALL_BASE_URL) + .queryParam("page", page) + .toUriString(); + HttpRequest httpRequest = HttpRequest.newBuilder() + .GET() + .uri(URI.create(url)) + .build(); + try { + HttpResponse response = httpClient + .send(httpRequest, HttpResponse.BodyHandlers.ofString()); + return objectMapper.readValue( + response.body(), + InternalCharListDto.class + ); + } catch (IOException | InterruptedException e) { + throw new RuntimeException(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..5f563984 --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/service/impl/CharacterServiceImpl.java @@ -0,0 +1,50 @@ +package mate.academy.rickandmorty.service.impl; + +import java.util.ArrayList; +import java.util.List; +import lombok.RequiredArgsConstructor; +import mate.academy.rickandmorty.dto.internal.InternalCharListDto; +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 mate.academy.rickandmorty.service.RickAndMortyClient; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class CharacterServiceImpl implements CharacterService { + private final RickAndMortyClient client; + private final CharacterRepository repository; + private final CharacterMapper mapper; + + @Override + public void save() { + List characters = new ArrayList<>(); + int pageAmount = 42; + for (int i = 1; i < pageAmount + 1; i++) { + InternalCharListDto dtoList = client.getAllCharacters(i); + if (repository.findAll().isEmpty()) { + characters.addAll( + dtoList.getResults().stream() + .map(mapper::toEntity) + .toList() + ); + } + } + repository.saveAll(characters); + } + + @Override + public List getCharactersBySearchString(String searchString) { + if (!repository.findAll().isEmpty()) { + return repository.findAll() + .stream() + .filter(character -> character.getName().toLowerCase() + .contains(searchString.toLowerCase())) + .toList(); + } + throw new RuntimeException("Can't get all characters with this search string: " + + searchString); + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 8b137891..1c000319 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1,8 @@ +spring.datasource.url=jdbc:mysql://localhost:3306/pr?serverTimeZone=UTC +spring.datasource.username=root +spring.datasource.password=Piltrafilla1] +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver +spring.jpa.hibernate.ddl-auto=validate +spring.jpa.show-sql=true +spring.liquibase.enabled=true diff --git a/src/main/resources/db/changelog/changes/01-create-characters-table.yaml b/src/main/resources/db/changelog/changes/01-create-characters-table.yaml new file mode 100644 index 00000000..7f17e858 --- /dev/null +++ b/src/main/resources/db/changelog/changes/01-create-characters-table.yaml @@ -0,0 +1,36 @@ +databaseChangeLog: + - changeSet: + id: create-characters-table + author: V.Sukhov + changes: + - createTable: + tableName: characters + columns: + - column: + name: id + type: bigint + autoIncrement: true + constraints: + nullable: false + primaryKey: true + - column: + name: external_id + type: bigint + constraints: + nullable: false + unique: true + - column: + name: name + type: varchar(255) + constraints: + nullable: false + - column: + name: status + type: varchar(65) + constraints: + nullable: false + - column: + name: gender + type: varchar(65) + constraints: + nullable: false diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml new file mode 100644 index 00000000..9078f53d --- /dev/null +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -0,0 +1,3 @@ +databaseChangeLog: + - includeAll: + path: db/changelog/changes/ 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() { + } } From af38232f75a82c5d35fd261cf4383d2c56b19c49 Mon Sep 17 00:00:00 2001 From: Watxesnavo Date: Wed, 25 Oct 2023 22:21:09 +0200 Subject: [PATCH 2/7] refactored --- .../controller/CharacterController.java | 14 +----- .../service/CharacterService.java | 3 ++ .../service/RickAndMortyClient.java | 45 ++----------------- .../service/impl/CharacterServiceImpl.java | 34 ++++++++++---- src/main/resources/application.properties | 2 + 5 files changed, 36 insertions(+), 62 deletions(-) diff --git a/src/main/java/mate/academy/rickandmorty/controller/CharacterController.java b/src/main/java/mate/academy/rickandmorty/controller/CharacterController.java index d140a179..f54796e0 100644 --- a/src/main/java/mate/academy/rickandmorty/controller/CharacterController.java +++ b/src/main/java/mate/academy/rickandmorty/controller/CharacterController.java @@ -8,7 +8,6 @@ import mate.academy.rickandmorty.mapper.CharacterMapper; import mate.academy.rickandmorty.model.Character; import mate.academy.rickandmorty.service.CharacterService; -import mate.academy.rickandmorty.service.RickAndMortyClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -19,23 +18,14 @@ @RequestMapping("/characters") @RequiredArgsConstructor public class CharacterController { - private final RickAndMortyClient client; private final CharacterService service; private final CharacterMapper mapper; @Operation(summary = "show random character", - description = "show and save random character from client db") + description = "show random character from db") @GetMapping("/random") public ExternalCharResponseDto getRandomCharacter() { - return client.getRandomCharacter(); - } - - @Operation(summary = "saving all characters from client to db", - description = "save all char info from client db to yours," - + " only to use once for the first time with empty table") - @GetMapping("/save") - public void save() { - service.save(); + return service.getRandomCharacter(); } @Operation(summary = "show character by search name parameter", diff --git a/src/main/java/mate/academy/rickandmorty/service/CharacterService.java b/src/main/java/mate/academy/rickandmorty/service/CharacterService.java index 479b051a..e0103236 100644 --- a/src/main/java/mate/academy/rickandmorty/service/CharacterService.java +++ b/src/main/java/mate/academy/rickandmorty/service/CharacterService.java @@ -1,10 +1,13 @@ package mate.academy.rickandmorty.service; import java.util.List; +import mate.academy.rickandmorty.dto.external.ExternalCharResponseDto; import mate.academy.rickandmorty.model.Character; public interface CharacterService { void save(); List getCharactersBySearchString(String searchString); + + ExternalCharResponseDto getRandomCharacter(); } diff --git a/src/main/java/mate/academy/rickandmorty/service/RickAndMortyClient.java b/src/main/java/mate/academy/rickandmorty/service/RickAndMortyClient.java index 9689eed9..b2a2e5f2 100644 --- a/src/main/java/mate/academy/rickandmorty/service/RickAndMortyClient.java +++ b/src/main/java/mate/academy/rickandmorty/service/RickAndMortyClient.java @@ -6,60 +6,23 @@ import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; -import java.util.Random; import lombok.RequiredArgsConstructor; -import mate.academy.rickandmorty.dto.external.ExternalCharResponseDto; import mate.academy.rickandmorty.dto.internal.InternalCharListDto; -import mate.academy.rickandmorty.dto.internal.InternalCharResponseDto; -import mate.academy.rickandmorty.mapper.CharacterMapper; -import mate.academy.rickandmorty.model.Character; -import mate.academy.rickandmorty.repository.CharacterRepository; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.web.util.UriComponentsBuilder; @Component @RequiredArgsConstructor public class RickAndMortyClient { - private static final String GET_CHAR_BASE_URL = "https://rickandmortyapi.com/api/character/%s"; - private static final String GET_ALL_BASE_URL = "https://rickandmortyapi.com/api/character"; - private final CharacterMapper characterMapper; private final ObjectMapper objectMapper; - private final CharacterRepository characterRepository; - - public ExternalCharResponseDto getRandomCharacter() { - Random random = new Random(); - int maxCharAmount = 826; - HttpClient httpClient = HttpClient.newHttpClient(); - String url = GET_CHAR_BASE_URL.formatted(String.valueOf(random.nextInt(maxCharAmount))); - HttpRequest httpRequest = HttpRequest.newBuilder() - .GET() - .uri(URI.create(url)) - .build(); - try { - HttpResponse response = httpClient - .send(httpRequest, HttpResponse.BodyHandlers.ofString()); - InternalCharResponseDto internalCharResponseDto = objectMapper.readValue( - response.body(), InternalCharResponseDto.class - ); - if (!characterRepository.findAll().isEmpty()) { - for (Character c : characterRepository.findAll()) { - if (c.getExternalId().equals(internalCharResponseDto.id())) { - return characterMapper.toDto(c); - } - } - } - Character saved = characterRepository - .save(characterMapper.toEntity(internalCharResponseDto)); - return characterMapper.toDto(saved); - } catch (IOException | InterruptedException e) { - throw new RuntimeException(e); - } - } + @Value("${rick-and-morty-api-get-all-url}") + private String getAllBaseUrl; public InternalCharListDto getAllCharacters(Integer page) { HttpClient httpClient = HttpClient.newHttpClient(); String url = UriComponentsBuilder - .fromHttpUrl(GET_ALL_BASE_URL) + .fromHttpUrl(getAllBaseUrl) .queryParam("page", page) .toUriString(); HttpRequest httpRequest = HttpRequest.newBuilder() 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 5f563984..f5a7fa5e 100644 --- a/src/main/java/mate/academy/rickandmorty/service/impl/CharacterServiceImpl.java +++ b/src/main/java/mate/academy/rickandmorty/service/impl/CharacterServiceImpl.java @@ -1,8 +1,11 @@ package mate.academy.rickandmorty.service.impl; +import jakarta.annotation.PostConstruct; import java.util.ArrayList; import java.util.List; +import java.util.Random; import lombok.RequiredArgsConstructor; +import mate.academy.rickandmorty.dto.external.ExternalCharResponseDto; import mate.academy.rickandmorty.dto.internal.InternalCharListDto; import mate.academy.rickandmorty.mapper.CharacterMapper; import mate.academy.rickandmorty.model.Character; @@ -19,20 +22,23 @@ public class CharacterServiceImpl implements CharacterService { private final CharacterMapper mapper; @Override + @PostConstruct public void save() { List characters = new ArrayList<>(); int pageAmount = 42; - for (int i = 1; i < pageAmount + 1; i++) { - InternalCharListDto dtoList = client.getAllCharacters(i); - if (repository.findAll().isEmpty()) { - characters.addAll( - dtoList.getResults().stream() - .map(mapper::toEntity) - .toList() - ); + if (repository.findAll().isEmpty()) { + for (int i = 1; i < pageAmount + 1; i++) { + InternalCharListDto dtoList = client.getAllCharacters(i); + if (repository.findAll().isEmpty()) { + characters.addAll( + dtoList.getResults().stream() + .map(mapper::toEntity) + .toList() + ); + } } + repository.saveAll(characters); } - repository.saveAll(characters); } @Override @@ -47,4 +53,14 @@ public List getCharactersBySearchString(String searchString) { throw new RuntimeException("Can't get all characters with this search string: " + searchString); } + + @Override + public ExternalCharResponseDto getRandomCharacter() { + Random random = new Random(); + int maxCharAmount = 826; + return mapper.toDto( + repository.findById(random.nextLong(maxCharAmount)) + .orElseThrow(RuntimeException::new) + ); + } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 1c000319..5f6977db 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -6,3 +6,5 @@ spring.jpa.hibernate.ddl-auto=validate spring.jpa.show-sql=true spring.liquibase.enabled=true + +rick-and-morty-api-get-all-url=https://rickandmortyapi.com/api/character From f5675496c326a75b9a9d07e9f21c14a20d83f00c Mon Sep 17 00:00:00 2001 From: Watxesnavo Date: Wed, 25 Oct 2023 22:24:09 +0200 Subject: [PATCH 3/7] refactored --- src/test/java/mate/academy/rickandmorty/ApplicationTests.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/test/java/mate/academy/rickandmorty/ApplicationTests.java b/src/test/java/mate/academy/rickandmorty/ApplicationTests.java index 4fe47c0d..ee226f5c 100644 --- a/src/test/java/mate/academy/rickandmorty/ApplicationTests.java +++ b/src/test/java/mate/academy/rickandmorty/ApplicationTests.java @@ -1,11 +1,7 @@ package mate.academy.rickandmorty; -import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class ApplicationTests { - @Test - void contextLoads() { - } } From 859c68f0aaf0fc250de6d4d860cbeee9741d110a Mon Sep 17 00:00:00 2001 From: Watxesnavo Date: Thu, 26 Oct 2023 15:06:17 +0200 Subject: [PATCH 4/7] refactored --- .../service/impl/CharacterServiceImpl.java | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) 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 f5a7fa5e..c04a5d88 100644 --- a/src/main/java/mate/academy/rickandmorty/service/impl/CharacterServiceImpl.java +++ b/src/main/java/mate/academy/rickandmorty/service/impl/CharacterServiceImpl.java @@ -1,7 +1,6 @@ package mate.academy.rickandmorty.service.impl; import jakarta.annotation.PostConstruct; -import java.util.ArrayList; import java.util.List; import java.util.Random; import lombok.RequiredArgsConstructor; @@ -17,6 +16,8 @@ @Service @RequiredArgsConstructor public class CharacterServiceImpl implements CharacterService { + private final static int CLIENT_API_CHAR_TABLE_PAGE_NUMBER = 42; + private final static int CLIENT_API_TOTAL_CHAR_AMOUNT = 826; private final RickAndMortyClient client; private final CharacterRepository repository; private final CharacterMapper mapper; @@ -24,18 +25,15 @@ public class CharacterServiceImpl implements CharacterService { @Override @PostConstruct public void save() { - List characters = new ArrayList<>(); - int pageAmount = 42; - if (repository.findAll().isEmpty()) { - for (int i = 1; i < pageAmount + 1; i++) { + List characters = repository.findAll(); + if (characters.isEmpty()) { + for (int i = 1; i < CLIENT_API_CHAR_TABLE_PAGE_NUMBER + 1; i++) { InternalCharListDto dtoList = client.getAllCharacters(i); - if (repository.findAll().isEmpty()) { - characters.addAll( - dtoList.getResults().stream() - .map(mapper::toEntity) - .toList() - ); - } + characters.addAll( + dtoList.getResults().stream() + .map(mapper::toEntity) + .toList() + ); } repository.saveAll(characters); } @@ -57,9 +55,8 @@ public List getCharactersBySearchString(String searchString) { @Override public ExternalCharResponseDto getRandomCharacter() { Random random = new Random(); - int maxCharAmount = 826; return mapper.toDto( - repository.findById(random.nextLong(maxCharAmount)) + repository.findById(random.nextLong(CLIENT_API_TOTAL_CHAR_AMOUNT)) .orElseThrow(RuntimeException::new) ); } From 5f81de97ea995e81f4b3488784f349d2fa63c425 Mon Sep 17 00:00:00 2001 From: Watxesnavo Date: Thu, 26 Oct 2023 15:12:03 +0200 Subject: [PATCH 5/7] refactored --- .../rickandmorty/service/impl/CharacterServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 c04a5d88..852d896d 100644 --- a/src/main/java/mate/academy/rickandmorty/service/impl/CharacterServiceImpl.java +++ b/src/main/java/mate/academy/rickandmorty/service/impl/CharacterServiceImpl.java @@ -16,8 +16,8 @@ @Service @RequiredArgsConstructor public class CharacterServiceImpl implements CharacterService { - private final static int CLIENT_API_CHAR_TABLE_PAGE_NUMBER = 42; - private final static int CLIENT_API_TOTAL_CHAR_AMOUNT = 826; + private static final int CLIENT_API_CHAR_TABLE_PAGE_NUMBER = 42; + private static final int CLIENT_API_TOTAL_CHAR_AMOUNT = 826; private final RickAndMortyClient client; private final CharacterRepository repository; private final CharacterMapper mapper; From fa336417a43be799ef0ccc32340c6044092d169e Mon Sep 17 00:00:00 2001 From: Watxesnavo Date: Sun, 29 Oct 2023 20:35:24 +0100 Subject: [PATCH 6/7] refactored --- .../academy/rickandmorty/dto/internal/InfoDto.java | 3 ++- .../rickandmorty/dto/internal/InternalCharListDto.java | 2 ++ .../rickandmorty/service/RickAndMortyClient.java | 1 + .../service/impl/CharacterServiceImpl.java | 10 ++++++---- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/java/mate/academy/rickandmorty/dto/internal/InfoDto.java b/src/main/java/mate/academy/rickandmorty/dto/internal/InfoDto.java index 85acdeb2..cd076747 100644 --- a/src/main/java/mate/academy/rickandmorty/dto/internal/InfoDto.java +++ b/src/main/java/mate/academy/rickandmorty/dto/internal/InfoDto.java @@ -1,5 +1,6 @@ package mate.academy.rickandmorty.dto.internal; public record InfoDto(Long count, - Long pages) { + Long pages, + String next) { } diff --git a/src/main/java/mate/academy/rickandmorty/dto/internal/InternalCharListDto.java b/src/main/java/mate/academy/rickandmorty/dto/internal/InternalCharListDto.java index 0436b27a..d5698430 100644 --- a/src/main/java/mate/academy/rickandmorty/dto/internal/InternalCharListDto.java +++ b/src/main/java/mate/academy/rickandmorty/dto/internal/InternalCharListDto.java @@ -6,6 +6,8 @@ @Data public class InternalCharListDto { + @JsonProperty("info") + private InfoDto info; @JsonProperty("results") private List results; } diff --git a/src/main/java/mate/academy/rickandmorty/service/RickAndMortyClient.java b/src/main/java/mate/academy/rickandmorty/service/RickAndMortyClient.java index b2a2e5f2..4c8887b3 100644 --- a/src/main/java/mate/academy/rickandmorty/service/RickAndMortyClient.java +++ b/src/main/java/mate/academy/rickandmorty/service/RickAndMortyClient.java @@ -7,6 +7,7 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; import lombok.RequiredArgsConstructor; +import mate.academy.rickandmorty.dto.internal.InfoDto; import mate.academy.rickandmorty.dto.internal.InternalCharListDto; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; 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 852d896d..21597982 100644 --- a/src/main/java/mate/academy/rickandmorty/service/impl/CharacterServiceImpl.java +++ b/src/main/java/mate/academy/rickandmorty/service/impl/CharacterServiceImpl.java @@ -16,8 +16,7 @@ @Service @RequiredArgsConstructor public class CharacterServiceImpl implements CharacterService { - private static final int CLIENT_API_CHAR_TABLE_PAGE_NUMBER = 42; - private static final int CLIENT_API_TOTAL_CHAR_AMOUNT = 826; + private long clientApiTotalCharAmount = 826; private final RickAndMortyClient client; private final CharacterRepository repository; private final CharacterMapper mapper; @@ -26,9 +25,12 @@ public class CharacterServiceImpl implements CharacterService { @PostConstruct public void save() { List characters = repository.findAll(); + long pageNum = 1; if (characters.isEmpty()) { - for (int i = 1; i < CLIENT_API_CHAR_TABLE_PAGE_NUMBER + 1; i++) { + for (int i = 1; i < pageNum + 1; i++) { InternalCharListDto dtoList = client.getAllCharacters(i); + pageNum = dtoList.getInfo().pages(); + clientApiTotalCharAmount = dtoList.getInfo().count(); characters.addAll( dtoList.getResults().stream() .map(mapper::toEntity) @@ -56,7 +58,7 @@ public List getCharactersBySearchString(String searchString) { public ExternalCharResponseDto getRandomCharacter() { Random random = new Random(); return mapper.toDto( - repository.findById(random.nextLong(CLIENT_API_TOTAL_CHAR_AMOUNT)) + repository.findById(random.nextLong(clientApiTotalCharAmount)) .orElseThrow(RuntimeException::new) ); } From ce14b83bfde4b2b6bb3b838901507d81b1942888 Mon Sep 17 00:00:00 2001 From: Watxesnavo Date: Sun, 29 Oct 2023 20:37:57 +0100 Subject: [PATCH 7/7] refactored --- .../mate/academy/rickandmorty/service/RickAndMortyClient.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/mate/academy/rickandmorty/service/RickAndMortyClient.java b/src/main/java/mate/academy/rickandmorty/service/RickAndMortyClient.java index 4c8887b3..b2a2e5f2 100644 --- a/src/main/java/mate/academy/rickandmorty/service/RickAndMortyClient.java +++ b/src/main/java/mate/academy/rickandmorty/service/RickAndMortyClient.java @@ -7,7 +7,6 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; import lombok.RequiredArgsConstructor; -import mate.academy.rickandmorty.dto.internal.InfoDto; import mate.academy.rickandmorty.dto.internal.InternalCharListDto; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component;