From b66427afcbdb474603c1b9cf67458fdfb6a6521b Mon Sep 17 00:00:00 2001 From: OD Date: Mon, 11 Nov 2024 22:43:55 +0200 Subject: [PATCH 1/3] result --- pom.xml | 50 +++++++++++++ .../rickandmorty/config/AppConfig.java | 15 ++++ .../rickandmorty/config/MapperConfig.java | 13 ++++ .../controller/CharacterController.java | 26 +++++++ .../mate/academy/rickandmorty/dto/ApiDto.java | 30 ++++++++ .../academy/rickandmorty/dto/ApiResponse.java | 18 +++++ .../rickandmorty/dto/CharacterDto.java | 12 ++++ .../rickandmorty/mapper/CharacterMapper.java | 34 +++++++++ .../academy/rickandmorty/model/Character.java | 23 ++++++ .../repository/CharacterRepository.java | 7 ++ .../service/CharacterService.java | 15 ++++ .../service/impl/CharacterServiceImpl.java | 70 +++++++++++++++++++ src/main/resources/application.properties | 9 ++- .../changes/01-create-characters-table.yaml | 27 +++++++ .../db/changelog/db.changelog-master.yaml | 3 + 15 files changed, 351 insertions(+), 1 deletion(-) create mode 100644 src/main/java/mate/academy/rickandmorty/config/AppConfig.java 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/ApiDto.java create mode 100644 src/main/java/mate/academy/rickandmorty/dto/ApiResponse.java create mode 100644 src/main/java/mate/academy/rickandmorty/dto/CharacterDto.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 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..33890d6b 100644 --- a/pom.xml +++ b/pom.xml @@ -15,12 +15,20 @@ jv-rick-and-morty 17 + 1.6.2 3.1.1 https://raw.githubusercontent.com/mate-academy/style-guides/master/java/checkstyle.xml + + com.mysql + mysql-connector-j + runtime + + + org.springframework.boot spring-boot-starter @@ -41,6 +49,48 @@ com.h2database h2 + + + org.mapstruct + mapstruct + ${mapstruct.version} + + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + provided + + + + org.projectlombok + lombok-mapstruct-binding + 0.2.0 + + + + org.springframework.boot + spring-boot-starter-web + + + + org.projectlombok + lombok + true + + + + org.liquibase + liquibase-core + ${liquibase.version} + + + + org.liquibase + liquibase-maven-plugin + ${liquibase.version} + diff --git a/src/main/java/mate/academy/rickandmorty/config/AppConfig.java b/src/main/java/mate/academy/rickandmorty/config/AppConfig.java new file mode 100644 index 00000000..2dda077c --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/config/AppConfig.java @@ -0,0 +1,15 @@ +package mate.academy.rickandmorty.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class AppConfig { + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} + 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..01edb6be --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/controller/CharacterController.java @@ -0,0 +1,26 @@ +package mate.academy.rickandmorty.controller; + +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.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/characters") +public class CharacterController { + private final CharacterService characterService; + + @GetMapping("/random") + public CharacterDto getRandomCharacter() { + return characterService.getRandomCharacter(); + } + + @GetMapping("/{id}") + public CharacterDto getCharacterById(@PathVariable Long id) { + return characterService.getCharacterById(id); + } +} diff --git a/src/main/java/mate/academy/rickandmorty/dto/ApiDto.java b/src/main/java/mate/academy/rickandmorty/dto/ApiDto.java new file mode 100644 index 00000000..bb4dd479 --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/dto/ApiDto.java @@ -0,0 +1,30 @@ +package mate.academy.rickandmorty.dto; + +import lombok.Data; + +@Data +public class ApiDto { + private int id; + private String name; + private String status; + private String species; + private String type; + private String gender; + private OriginDto origin; + private LocationDto location; + private String image; + private String url; + private String created; + + @Data + public static class OriginDto { + private String name; + private String url; + } + + @Data + public static class LocationDto { + private String name; + private String url; + } +} diff --git a/src/main/java/mate/academy/rickandmorty/dto/ApiResponse.java b/src/main/java/mate/academy/rickandmorty/dto/ApiResponse.java new file mode 100644 index 00000000..83434c96 --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/dto/ApiResponse.java @@ -0,0 +1,18 @@ +package mate.academy.rickandmorty.dto; + +import java.util.List; +import lombok.Data; + +@Data +public class ApiResponse { + private Info info; + private List results; + + @Data + public static class Info { + private int count; + private int pages; + private String next; + private String prev; + } +} 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..d4f555df --- /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 internalId; + private String name; + private String status; + private 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..fd94b957 --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/mapper/CharacterMapper.java @@ -0,0 +1,34 @@ +package mate.academy.rickandmorty.mapper; + +import mate.academy.rickandmorty.dto.ApiDto; +import mate.academy.rickandmorty.dto.CharacterDto; +import mate.academy.rickandmorty.model.Character; +import org.springframework.stereotype.Component; + +@Component +public class CharacterMapper { + public CharacterDto toDto(Character character) { + if (character == null) { + return null; + } + CharacterDto characterDto = new CharacterDto(); + characterDto.setId(character.getId()); + characterDto.setName(character.getName()); + characterDto.setStatus(character.getStatus()); + characterDto.setGender(character.getGender()); + characterDto.setInternalId(character.getInternalId()); + return characterDto; + } + + public Character toEntity(ApiDto apiDto) { + if (apiDto == null) { + return null; + } + Character character = new Character(); + character.setName(apiDto.getName()); + character.setStatus(apiDto.getStatus()); + character.setGender(apiDto.getGender()); + character.setInternalId(String.valueOf(apiDto.getId())); + return 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..c97b1662 --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/model/Character.java @@ -0,0 +1,23 @@ +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.Getter; +import lombok.Setter; + +@Entity +@Getter +@Setter +@Table(name = "characters") +public class Character { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String internalId; + 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..b893e057 --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/repository/CharacterRepository.java @@ -0,0 +1,7 @@ +package mate.academy.rickandmorty.repository; + +import mate.academy.rickandmorty.model.Character; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface CharacterRepository extends JpaRepository { +} 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..3e3639e4 --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/service/CharacterService.java @@ -0,0 +1,15 @@ +package mate.academy.rickandmorty.service; + +import java.util.List; +import mate.academy.rickandmorty.dto.ApiDto; +import mate.academy.rickandmorty.dto.CharacterDto; + +public interface CharacterService { + CharacterDto getRandomCharacter(); + + CharacterDto getCharacterById(Long id); + + List fetchAllCharacters(); + + void saveAllCharactersToDb(); +} 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..418e8eca --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/service/impl/CharacterServiceImpl.java @@ -0,0 +1,70 @@ +package mate.academy.rickandmorty.service.impl; + +import jakarta.annotation.PostConstruct; +import jakarta.persistence.EntityNotFoundException; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import mate.academy.rickandmorty.dto.ApiDto; +import mate.academy.rickandmorty.dto.ApiResponse; +import mate.academy.rickandmorty.dto.CharacterDto; +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.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +@Service +@RequiredArgsConstructor +public class CharacterServiceImpl implements CharacterService { + public static final Long CHARACTER_MAX = 826L; + private final CharacterMapper characterMapper; + private final CharacterRepository characterRepository; + private final RestTemplate restTemplate; + + @Override + public CharacterDto getRandomCharacter() { + Random random = new Random(); + return getCharacterById(random.nextLong(CHARACTER_MAX) + 1); + } + + @Override + public CharacterDto getCharacterById(Long id) { + return characterMapper.toDto(characterRepository.findById(id).orElseThrow(() + -> new EntityNotFoundException("Can't find character by id " + id))); + } + + @Override + public List fetchAllCharacters() { + String url = "https://rickandmortyapi.com/api/character?page="; + List allCharacters = new ArrayList<>(); + int page = 1; + ApiResponse response; + + do { + ResponseEntity apiResponse = restTemplate + .getForEntity(url + page, ApiResponse.class); + response = apiResponse.getBody(); + if (response != null && response.getResults() != null) { + allCharacters.addAll(response.getResults()); + } + page++; + } while (response != null && response.getInfo().getNext() != null); + + return allCharacters; + } + + @PostConstruct + @Override + public void saveAllCharactersToDb() { + List characters = fetchAllCharacters(); + List characterEntities = characters.stream() + .map((ApiDto characterDto) -> characterMapper.toEntity(characterDto)) + .collect(Collectors.toList()); + characterRepository.saveAll(characterEntities); + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 8b137891..121be6dc 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1,8 @@ - +spring.datasource.url=jdbc:mysql://localhost:3306/rick_and_morty +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect +spring.datasource.username= +spring.datasource.password= +spring.jpa.hibernate.ddl-auto=validate +spring.jpa.show-sql=true +spring.jpa.open-in-view=false 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..97256c9e --- /dev/null +++ b/src/main/resources/db/changelog/changes/01-create-characters-table.yaml @@ -0,0 +1,27 @@ +databaseChangeLog: + - changeSet: + id: create-characters-table + author: SD + changes: + - createTable: + tableName: characters + columns: + - column: + name: id + type: BIGINT + autoIncrement: true + constraints: + primaryKey: true + nullable: false + - column: + name: internal_id + type: varchar(255) + - column: + name: name + type: varchar(255) + - column: + name: status + type: varchar(255) + - column: + name: gender + type: varchar(255) 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..1187add3 --- /dev/null +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -0,0 +1,3 @@ +databaseChangeLog: + - include: + file: db/changelog/changes/01-create-characters-table.yaml \ No newline at end of file From 29986c58648f3adc053eb86f3f2656ab3110e7de Mon Sep 17 00:00:00 2001 From: OD Date: Tue, 12 Nov 2024 18:08:54 +0200 Subject: [PATCH 2/3] fixed --- pom.xml | 14 +++++++ .../academy/rickandmorty/Application.java | 1 - ...AppConfig.java => RestTemplateConfig.java} | 3 +- .../mate/academy/rickandmorty/dto/ApiDto.java | 30 --------------- .../academy/rickandmorty/dto/ApiResponse.java | 18 --------- .../dto/CharacterExternalDto.java | 11 ++++++ .../dto/CharacterResponseDto.java | 11 ++++++ .../rickandmorty/mapper/CharacterMapper.java | 38 ++++++------------- .../academy/rickandmorty/model/Character.java | 2 + .../service/CharacterService.java | 4 +- .../service/impl/CharacterServiceImpl.java | 25 ++++++------ .../changes/01-create-characters-table.yaml | 3 ++ 12 files changed, 68 insertions(+), 92 deletions(-) rename src/main/java/mate/academy/rickandmorty/config/{AppConfig.java => RestTemplateConfig.java} (90%) delete mode 100644 src/main/java/mate/academy/rickandmorty/dto/ApiDto.java delete mode 100644 src/main/java/mate/academy/rickandmorty/dto/ApiResponse.java create mode 100644 src/main/java/mate/academy/rickandmorty/dto/CharacterExternalDto.java create mode 100644 src/main/java/mate/academy/rickandmorty/dto/CharacterResponseDto.java diff --git a/pom.xml b/pom.xml index 33890d6b..e17a92f5 100644 --- a/pom.xml +++ b/pom.xml @@ -40,6 +40,17 @@ test + + org.springframework + spring-web + + + + + org.springframework.boot + spring-boot-starter-web + + org.springframework.boot spring-boot-starter-data-jpa @@ -116,6 +127,9 @@ true true false + + src/main/java + diff --git a/src/main/java/mate/academy/rickandmorty/Application.java b/src/main/java/mate/academy/rickandmorty/Application.java index cdea84fc..3b4e97e6 100644 --- a/src/main/java/mate/academy/rickandmorty/Application.java +++ b/src/main/java/mate/academy/rickandmorty/Application.java @@ -5,7 +5,6 @@ @SpringBootApplication public class Application { - public static void main(String[] args) { SpringApplication.run(Application.class, args); } diff --git a/src/main/java/mate/academy/rickandmorty/config/AppConfig.java b/src/main/java/mate/academy/rickandmorty/config/RestTemplateConfig.java similarity index 90% rename from src/main/java/mate/academy/rickandmorty/config/AppConfig.java rename to src/main/java/mate/academy/rickandmorty/config/RestTemplateConfig.java index 2dda077c..4879b984 100644 --- a/src/main/java/mate/academy/rickandmorty/config/AppConfig.java +++ b/src/main/java/mate/academy/rickandmorty/config/RestTemplateConfig.java @@ -5,8 +5,7 @@ import org.springframework.web.client.RestTemplate; @Configuration -public class AppConfig { - +public class RestTemplateConfig { @Bean public RestTemplate restTemplate() { return new RestTemplate(); diff --git a/src/main/java/mate/academy/rickandmorty/dto/ApiDto.java b/src/main/java/mate/academy/rickandmorty/dto/ApiDto.java deleted file mode 100644 index bb4dd479..00000000 --- a/src/main/java/mate/academy/rickandmorty/dto/ApiDto.java +++ /dev/null @@ -1,30 +0,0 @@ -package mate.academy.rickandmorty.dto; - -import lombok.Data; - -@Data -public class ApiDto { - private int id; - private String name; - private String status; - private String species; - private String type; - private String gender; - private OriginDto origin; - private LocationDto location; - private String image; - private String url; - private String created; - - @Data - public static class OriginDto { - private String name; - private String url; - } - - @Data - public static class LocationDto { - private String name; - private String url; - } -} diff --git a/src/main/java/mate/academy/rickandmorty/dto/ApiResponse.java b/src/main/java/mate/academy/rickandmorty/dto/ApiResponse.java deleted file mode 100644 index 83434c96..00000000 --- a/src/main/java/mate/academy/rickandmorty/dto/ApiResponse.java +++ /dev/null @@ -1,18 +0,0 @@ -package mate.academy.rickandmorty.dto; - -import java.util.List; -import lombok.Data; - -@Data -public class ApiResponse { - private Info info; - private List results; - - @Data - public static class Info { - private int count; - private int pages; - private String next; - private String prev; - } -} diff --git a/src/main/java/mate/academy/rickandmorty/dto/CharacterExternalDto.java b/src/main/java/mate/academy/rickandmorty/dto/CharacterExternalDto.java new file mode 100644 index 00000000..8c85393f --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/dto/CharacterExternalDto.java @@ -0,0 +1,11 @@ +package mate.academy.rickandmorty.dto; + +import lombok.Data; + +@Data +public class CharacterExternalDto { + private Long id; + 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..c3fc773d --- /dev/null +++ b/src/main/java/mate/academy/rickandmorty/dto/CharacterResponseDto.java @@ -0,0 +1,11 @@ +package mate.academy.rickandmorty.dto; + +import java.util.List; +import java.util.Map; +import lombok.Data; + +@Data +public class CharacterResponseDto { + private Map info; + 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 index fd94b957..1a8c2466 100644 --- a/src/main/java/mate/academy/rickandmorty/mapper/CharacterMapper.java +++ b/src/main/java/mate/academy/rickandmorty/mapper/CharacterMapper.java @@ -1,34 +1,20 @@ package mate.academy.rickandmorty.mapper; -import mate.academy.rickandmorty.dto.ApiDto; +import mate.academy.rickandmorty.config.MapperConfig; import mate.academy.rickandmorty.dto.CharacterDto; +import mate.academy.rickandmorty.dto.CharacterExternalDto; import mate.academy.rickandmorty.model.Character; -import org.springframework.stereotype.Component; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; -@Component -public class CharacterMapper { - public CharacterDto toDto(Character character) { - if (character == null) { - return null; - } - CharacterDto characterDto = new CharacterDto(); - characterDto.setId(character.getId()); - characterDto.setName(character.getName()); - characterDto.setStatus(character.getStatus()); - characterDto.setGender(character.getGender()); - characterDto.setInternalId(character.getInternalId()); - return characterDto; - } +@Mapper(config = MapperConfig.class) +public interface CharacterMapper { + CharacterDto toDto(Character character); + + @Mapping(target = "internalId", expression = "java(generateInternalId(characterResponseDto))") + Character toEntity(CharacterExternalDto characterResponseDto); - public Character toEntity(ApiDto apiDto) { - if (apiDto == null) { - return null; - } - Character character = new Character(); - character.setName(apiDto.getName()); - character.setStatus(apiDto.getStatus()); - character.setGender(apiDto.getGender()); - character.setInternalId(String.valueOf(apiDto.getId())); - return character; + default String generateInternalId(CharacterExternalDto dto) { + return dto.getId() != null ? String.valueOf(dto.getId()) : "default-id"; } } diff --git a/src/main/java/mate/academy/rickandmorty/model/Character.java b/src/main/java/mate/academy/rickandmorty/model/Character.java index c97b1662..0ea05919 100644 --- a/src/main/java/mate/academy/rickandmorty/model/Character.java +++ b/src/main/java/mate/academy/rickandmorty/model/Character.java @@ -1,5 +1,6 @@ package mate.academy.rickandmorty.model; +import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; @@ -16,6 +17,7 @@ public class Character { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @Column(nullable = false, unique = true) private String internalId; private String name; private String status; diff --git a/src/main/java/mate/academy/rickandmorty/service/CharacterService.java b/src/main/java/mate/academy/rickandmorty/service/CharacterService.java index 3e3639e4..849a0c4d 100644 --- a/src/main/java/mate/academy/rickandmorty/service/CharacterService.java +++ b/src/main/java/mate/academy/rickandmorty/service/CharacterService.java @@ -1,15 +1,15 @@ package mate.academy.rickandmorty.service; import java.util.List; -import mate.academy.rickandmorty.dto.ApiDto; import mate.academy.rickandmorty.dto.CharacterDto; +import mate.academy.rickandmorty.dto.CharacterExternalDto; public interface CharacterService { CharacterDto getRandomCharacter(); CharacterDto getCharacterById(Long id); - List fetchAllCharacters(); + List fetchAllCharacters(); void saveAllCharactersToDb(); } 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 418e8eca..a18e2cf7 100644 --- a/src/main/java/mate/academy/rickandmorty/service/impl/CharacterServiceImpl.java +++ b/src/main/java/mate/academy/rickandmorty/service/impl/CharacterServiceImpl.java @@ -7,9 +7,9 @@ import java.util.Random; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; -import mate.academy.rickandmorty.dto.ApiDto; -import mate.academy.rickandmorty.dto.ApiResponse; import mate.academy.rickandmorty.dto.CharacterDto; +import mate.academy.rickandmorty.dto.CharacterExternalDto; +import mate.academy.rickandmorty.dto.CharacterResponseDto; import mate.academy.rickandmorty.mapper.CharacterMapper; import mate.academy.rickandmorty.model.Character; import mate.academy.rickandmorty.repository.CharacterRepository; @@ -21,6 +21,7 @@ @Service @RequiredArgsConstructor public class CharacterServiceImpl implements CharacterService { + public static final String URL = "https://rickandmortyapi.com/api/character?page="; public static final Long CHARACTER_MAX = 826L; private final CharacterMapper characterMapper; private final CharacterRepository characterRepository; @@ -39,31 +40,29 @@ public CharacterDto getCharacterById(Long id) { } @Override - public List fetchAllCharacters() { - String url = "https://rickandmortyapi.com/api/character?page="; - List allCharacters = new ArrayList<>(); + public List fetchAllCharacters() { + List allCharacters = new ArrayList<>(); int page = 1; - ApiResponse response; - + CharacterResponseDto response; do { - ResponseEntity apiResponse = restTemplate - .getForEntity(url + page, ApiResponse.class); + ResponseEntity apiResponse = restTemplate + .getForEntity(URL + page, CharacterResponseDto.class); response = apiResponse.getBody(); if (response != null && response.getResults() != null) { allCharacters.addAll(response.getResults()); } + String nextPageUrl = (String) response.getInfo().get("next"); page++; - } while (response != null && response.getInfo().getNext() != null); - + } while (response != null && response.getInfo().get("next") != null); return allCharacters; } @PostConstruct @Override public void saveAllCharactersToDb() { - List characters = fetchAllCharacters(); + List characters = fetchAllCharacters(); List characterEntities = characters.stream() - .map((ApiDto characterDto) -> characterMapper.toEntity(characterDto)) + .map((CharacterExternalDto characterDto) -> characterMapper.toEntity(characterDto)) .collect(Collectors.toList()); characterRepository.saveAll(characterEntities); } 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 index 97256c9e..40e22f16 100644 --- a/src/main/resources/db/changelog/changes/01-create-characters-table.yaml +++ b/src/main/resources/db/changelog/changes/01-create-characters-table.yaml @@ -16,6 +16,9 @@ databaseChangeLog: - column: name: internal_id type: varchar(255) + constraints: + unique: true + nullable: false - column: name: name type: varchar(255) From 5f9f508804e9f622c5cf91151a686f3af6e93675 Mon Sep 17 00:00:00 2001 From: OD Date: Wed, 13 Nov 2024 00:04:49 +0200 Subject: [PATCH 3/3] fixed added/removed lines in CharacterServiceImpl, db.changelog-master, pom added constants in CharacterServiceImpl, CharacterMapper --- pom.xml | 2 -- .../rickandmorty/mapper/CharacterMapper.java | 4 ++- .../service/impl/CharacterServiceImpl.java | 27 +++++++++++-------- .../db/changelog/db.changelog-master.yaml | 2 +- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/pom.xml b/pom.xml index e17a92f5..51de08fd 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,6 @@ runtime - org.springframework.boot spring-boot-starter @@ -45,7 +44,6 @@ spring-web - org.springframework.boot spring-boot-starter-web diff --git a/src/main/java/mate/academy/rickandmorty/mapper/CharacterMapper.java b/src/main/java/mate/academy/rickandmorty/mapper/CharacterMapper.java index 1a8c2466..aba57ca5 100644 --- a/src/main/java/mate/academy/rickandmorty/mapper/CharacterMapper.java +++ b/src/main/java/mate/academy/rickandmorty/mapper/CharacterMapper.java @@ -9,12 +9,14 @@ @Mapper(config = MapperConfig.class) public interface CharacterMapper { + String DEFAULT_ID = "default-id"; + CharacterDto toDto(Character character); @Mapping(target = "internalId", expression = "java(generateInternalId(characterResponseDto))") Character toEntity(CharacterExternalDto characterResponseDto); default String generateInternalId(CharacterExternalDto dto) { - return dto.getId() != null ? String.valueOf(dto.getId()) : "default-id"; + return dto.getId() != null ? String.valueOf(dto.getId()) : DEFAULT_ID; } } 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 a18e2cf7..689d8cd2 100644 --- a/src/main/java/mate/academy/rickandmorty/service/impl/CharacterServiceImpl.java +++ b/src/main/java/mate/academy/rickandmorty/service/impl/CharacterServiceImpl.java @@ -1,6 +1,5 @@ package mate.academy.rickandmorty.service.impl; -import jakarta.annotation.PostConstruct; import jakarta.persistence.EntityNotFoundException; import java.util.ArrayList; import java.util.List; @@ -14,6 +13,8 @@ import mate.academy.rickandmorty.model.Character; import mate.academy.rickandmorty.repository.CharacterRepository; import mate.academy.rickandmorty.service.CharacterService; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.event.EventListener; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; @@ -21,16 +22,17 @@ @Service @RequiredArgsConstructor public class CharacterServiceImpl implements CharacterService { - public static final String URL = "https://rickandmortyapi.com/api/character?page="; - public static final Long CHARACTER_MAX = 826L; + private static final String URL = "https://rickandmortyapi.com/api/character?page="; + private static final Long CHARACTER_MAX = 826L; + private static final String URL_NEXT_KEY = "next"; + private static final Random RANDOM = new Random(); private final CharacterMapper characterMapper; private final CharacterRepository characterRepository; private final RestTemplate restTemplate; @Override public CharacterDto getRandomCharacter() { - Random random = new Random(); - return getCharacterById(random.nextLong(CHARACTER_MAX) + 1); + return getCharacterById(RANDOM.nextLong(CHARACTER_MAX) + 1); } @Override @@ -45,24 +47,27 @@ public List fetchAllCharacters() { int page = 1; CharacterResponseDto response; do { - ResponseEntity apiResponse = restTemplate - .getForEntity(URL + page, CharacterResponseDto.class); + ResponseEntity apiResponse = restTemplate.getForEntity( + URL + page, CharacterResponseDto.class + ); response = apiResponse.getBody(); + if (response != null && response.getResults() != null) { allCharacters.addAll(response.getResults()); } - String nextPageUrl = (String) response.getInfo().get("next"); + + String nextPageUrl = (String) response.getInfo().get(URL_NEXT_KEY); page++; - } while (response != null && response.getInfo().get("next") != null); + } while (response != null && response.getInfo().get(URL_NEXT_KEY) != null); return allCharacters; } - @PostConstruct + @EventListener(ApplicationReadyEvent.class) @Override public void saveAllCharactersToDb() { List characters = fetchAllCharacters(); List characterEntities = characters.stream() - .map((CharacterExternalDto characterDto) -> characterMapper.toEntity(characterDto)) + .map(characterMapper::toEntity) .collect(Collectors.toList()); characterRepository.saveAll(characterEntities); } diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index 1187add3..f26dd0e8 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -1,3 +1,3 @@ databaseChangeLog: - include: - file: db/changelog/changes/01-create-characters-table.yaml \ No newline at end of file + file: db/changelog/changes/01-create-characters-table.yaml