-
Notifications
You must be signed in to change notification settings - Fork 308
Solution #202
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Solution #202
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 = "<PACKAGE_NAME>.impl" | ||
) | ||
public class MapperConfig { | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -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 lombok.RequiredArgsConstructor; | ||||||||||||||||||
import mate.academy.rickandmorty.dto.internal.InternalHeroDto; | ||||||||||||||||||
import mate.academy.rickandmorty.service.RickAndMortyService; | ||||||||||||||||||
import org.springframework.web.bind.annotation.GetMapping; | ||||||||||||||||||
import org.springframework.web.bind.annotation.RequestMapping; | ||||||||||||||||||
import org.springframework.web.bind.annotation.RestController; | ||||||||||||||||||
|
||||||||||||||||||
@Tag(name = "Rick&MortyApi", description = "") | ||||||||||||||||||
@RequiredArgsConstructor | ||||||||||||||||||
@RestController | ||||||||||||||||||
@RequestMapping("/character") | ||||||||||||||||||
public class RickAndMortyController { | ||||||||||||||||||
private final RickAndMortyService rickAndMortyService; | ||||||||||||||||||
|
||||||||||||||||||
@Operation(summary = "Add random hero", description = """ | ||||||||||||||||||
The request randomly generates a wiki about one character in | ||||||||||||||||||
the universe the animated series Rick & Morty.""") | ||||||||||||||||||
|
||||||||||||||||||
@GetMapping("/get") | ||||||||||||||||||
|
@GetMapping("/get") | |
@GetMapping("/random") |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You use 2 different names: hero and character for the same thing through all your project. Please, stick to one of them, because it can be confusing
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- You don't use a @PathVariable or @RequestParam, where do you get parameter "name" from?)
@GetMapping("/find") | |
List<InternalHeroDto> heroesByName(String name) { | |
return rickAndMortyService.findByName(name); | |
} | |
@GetMapping | |
List<InternalHeroDto> getCharacterByName(@RequestParam String name) { | |
return rickAndMortyService.findByName(name); | |
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package mate.academy.rickandmorty.dto.external; | ||
|
||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
|
||
public record ExternalHeroDto(@JsonProperty("id") | ||
Long externalId, | ||
String name, | ||
String status, | ||
String gender | ||
) { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package mate.academy.rickandmorty.dto.external; | ||
|
||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
import java.util.List; | ||
|
||
public record ExternalHeroResponseDto( | ||
@JsonProperty("results") | ||
List<ExternalHeroDto> result) { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package mate.academy.rickandmorty.dto.internal; | ||
|
||
public record InternalHeroDto(Long id, | ||
Long externalId, | ||
String name, | ||
String status, | ||
String gender) { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package mate.academy.rickandmorty.mapper; | ||
|
||
import mate.academy.rickandmorty.config.MapperConfig; | ||
import mate.academy.rickandmorty.dto.external.ExternalHeroDto; | ||
import mate.academy.rickandmorty.dto.internal.InternalHeroDto; | ||
import mate.academy.rickandmorty.model.Hero; | ||
import org.mapstruct.Mapper; | ||
|
||
@Mapper(config = MapperConfig.class) | ||
public interface HeroMapper { | ||
|
||
InternalHeroDto toDto(Hero hero); | ||
|
||
Hero toEntity(ExternalHeroDto dto); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
|
||
@Getter | ||
@Setter | ||
@Entity | ||
@Table(name = "heroes") | ||
public class Hero { | ||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Long id; | ||
private Long externalId; | ||
private String name; | ||
private String status; | ||
private String gender; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package mate.academy.rickandmorty.repository; | ||
|
||
import mate.academy.rickandmorty.model.Hero; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
|
||
public interface HeroRepository extends JpaRepository<Hero, Long> { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package mate.academy.rickandmorty.service; | ||
|
||
import java.util.List; | ||
|
||
public interface ClientApi<T> { | ||
T getRandomHero(int randomNumber); | ||
|
||
List<T> findHeroesByName(String heroName); | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,59 @@ | ||||||||||||
package mate.academy.rickandmorty.service; | ||||||||||||
|
||||||||||||
import com.fasterxml.jackson.databind.ObjectMapper; | ||||||||||||
import java.io.IOException; | ||||||||||||
import java.net.URI; | ||||||||||||
import java.net.http.HttpClient; | ||||||||||||
import java.net.http.HttpRequest; | ||||||||||||
import java.net.http.HttpResponse; | ||||||||||||
import java.util.List; | ||||||||||||
import lombok.RequiredArgsConstructor; | ||||||||||||
import mate.academy.rickandmorty.dto.external.ExternalHeroDto; | ||||||||||||
import mate.academy.rickandmorty.dto.external.ExternalHeroResponseDto; | ||||||||||||
import org.springframework.stereotype.Component; | ||||||||||||
|
||||||||||||
@RequiredArgsConstructor | ||||||||||||
@Component | ||||||||||||
public class RickAndMortyClientImpl implements ClientApi<ExternalHeroDto> { | ||||||||||||
private static final String BASE_URL = "https://rickandmortyapi.com/api/character"; | ||||||||||||
private static final String BY_ID = "%s/%d"; | ||||||||||||
private static final String BY_NAME = "%s?name=%s"; | ||||||||||||
private final ObjectMapper objectMapper; | ||||||||||||
|
||||||||||||
@Override | ||||||||||||
public ExternalHeroDto getRandomHero(int randomNumber) { | ||||||||||||
HttpClient httpClient = HttpClient.newHttpClient(); | ||||||||||||
|
||||||||||||
String url = String.format(BY_ID, BASE_URL, randomNumber); | ||||||||||||
HttpRequest httpRequest = initRequest(url); | ||||||||||||
try { | ||||||||||||
HttpResponse<String> response = httpClient | ||||||||||||
.send(httpRequest, HttpResponse.BodyHandlers.ofString()); | ||||||||||||
return objectMapper | ||||||||||||
.readValue(response.body(), ExternalHeroDto.class); | ||||||||||||
} catch (IOException | InterruptedException e) { | ||||||||||||
throw new RuntimeException(e); | ||||||||||||
|
||||||||||||
} | ||||||||||||
} | ||||||||||||
|
||||||||||||
@Override | ||||||||||||
public List<ExternalHeroDto> findHeroesByName(String characterName) { | ||||||||||||
HttpClient httpClient = HttpClient.newHttpClient(); | ||||||||||||
String url = String.format(BY_NAME, BASE_URL, characterName); | ||||||||||||
HttpRequest httpRequest = initRequest(url); | ||||||||||||
try { | ||||||||||||
HttpResponse<String> response = httpClient | ||||||||||||
.send(httpRequest, HttpResponse.BodyHandlers.ofString()); | ||||||||||||
return objectMapper | ||||||||||||
.readValue(response.body(), ExternalHeroResponseDto.class).result(); | ||||||||||||
|
return objectMapper | |
.readValue(response.body(), ExternalHeroResponseDto.class).result(); | |
return objectMapper | |
.readValue(response.body(), ExternalHeroResponseDto.class) | |
.result(); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package mate.academy.rickandmorty.service; | ||
|
||
import java.util.List; | ||
import mate.academy.rickandmorty.dto.internal.InternalHeroDto; | ||
|
||
public interface RickAndMortyService { | ||
InternalHeroDto getRandomCharacter(); | ||
|
||
List<InternalHeroDto> findByName(String name); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package mate.academy.rickandmorty.service; | ||
|
||
import java.util.List; | ||
import java.util.Random; | ||
import lombok.RequiredArgsConstructor; | ||
import mate.academy.rickandmorty.dto.external.ExternalHeroDto; | ||
import mate.academy.rickandmorty.dto.internal.InternalHeroDto; | ||
import mate.academy.rickandmorty.mapper.HeroMapper; | ||
import mate.academy.rickandmorty.model.Hero; | ||
import mate.academy.rickandmorty.repository.HeroRepository; | ||
import org.springframework.stereotype.Service; | ||
|
||
@RequiredArgsConstructor | ||
@Service | ||
public class RickAndMortyServiceImpl implements RickAndMortyService { | ||
private static final int API_COUNT_HEROES = 800; | ||
private final ClientApi<ExternalHeroDto> rickAndMortyClient; | ||
private final HeroMapper heroMapper; | ||
private final HeroRepository heroRepository; | ||
|
||
@Override | ||
public InternalHeroDto getRandomCharacter() { | ||
ExternalHeroDto randomHero = rickAndMortyClient | ||
.getRandomHero(new Random().nextInt(API_COUNT_HEROES)); | ||
|
||
Hero entity = heroMapper.toEntity(randomHero); | ||
return heroMapper.toDto(heroRepository.save(entity)); | ||
} | ||
|
||
@Override | ||
public List<InternalHeroDto> findByName(String name) { | ||
return rickAndMortyClient | ||
.findHeroesByName(name).stream() | ||
.map(e -> { | ||
Hero entity = heroMapper.toEntity(e); | ||
return heroMapper.toDto(heroRepository.save(entity)); | ||
}) | ||
.toList(); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,8 @@ | ||
spring.datasource.url=jdbc:mysql://localhost:3306/homework | ||
spring.datasource.username=root | ||
spring.datasource.password=databasepractice1! | ||
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver | ||
spring.jpa.hibernate.ddl-auto=create-drop | ||
|
||
spring.jpa.show-sql=true | ||
spring.jpa.open-in-view=false |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,9 +5,7 @@ | |
|
||
@SpringBootTest | ||
class ApplicationTests { | ||
|
||
@Test | ||
void contextLoads() { | ||
} | ||
|
||
@Test | ||
void contextLoads() { | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Empty description is not very good