-
Notifications
You must be signed in to change notification settings - Fork 237
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
Solution #200
base: main
Are you sure you want to change the base?
Solution #200
Changes from 1 commit
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,29 @@ | ||
package mate.academy.rickandmorty.config; | ||
|
||
import java.util.Random; | ||
import mate.academy.rickandmorty.service.CharacterService; | ||
import org.springframework.boot.CommandLineRunner; | ||
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(); | ||
} | ||
|
||
@Bean | ||
public Random random() { | ||
return new Random(); | ||
} | ||
|
||
@Bean | ||
public CommandLineRunner commandLineRunner(CharacterService characterService) { | ||
return args -> { | ||
characterService.fetchCharacters(); | ||
}; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
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 java.util.Optional; | ||
import lombok.RequiredArgsConstructor; | ||
import mate.academy.rickandmorty.model.Character; | ||
import mate.academy.rickandmorty.service.CharacterService; | ||
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; | ||
|
||
@RestController | ||
@RequiredArgsConstructor | ||
@RequestMapping(value = "/api/characters") | ||
@Tag(name = "Character API", description = "Operations related to characters from Rick and Morty") | ||
public class CharacterController { | ||
private final CharacterService characterService; | ||
|
||
@GetMapping("/random") | ||
@Operation(summary = "Get a random character", | ||
description = "Returns a random character from the database.") | ||
public Optional<Character> getRandomCharacter() { | ||
return characterService.getRandomCharacter(); | ||
} | ||
|
||
@GetMapping("/search") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @GetMapping |
||
@Operation(summary = "Search characters by name", | ||
description = "Returns a list of characters whose name contains the specified word.") | ||
public List<Character> searchCharactersByName(@RequestParam String word) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here, return list of DTO responses |
||
return characterService.searchCharactersByName(word); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
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 jakarta.validation.constraints.NotNull; | ||
import lombok.Getter; | ||
import lombok.Setter; | ||
|
||
@Getter | ||
@Setter | ||
@Entity | ||
@Table(name = "characters") | ||
public class Character { | ||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Long id; | ||
private int externalId; | ||
@NotNull | ||
private String name; | ||
private String status; | ||
private String gender; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package mate.academy.rickandmorty.model; | ||
|
||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
import lombok.Getter; | ||
import lombok.Setter; | ||
|
||
@Getter | ||
@Setter | ||
public class RickAndMorty { | ||
@JsonProperty("id") | ||
private int id; | ||
@JsonProperty("name") | ||
private String name; | ||
@JsonProperty("status") | ||
private String status; | ||
@JsonProperty("gender") | ||
private String gender; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package mate.academy.rickandmorty.model; | ||
|
||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
import java.util.List; | ||
import lombok.Getter; | ||
import lombok.Setter; | ||
|
||
@Getter | ||
@Setter | ||
public class RickAndMortyResponse { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This class name is just the name of whole app, it's not clear why RickAndMorty fetches all characters, it's more like CharacterListDto or something like that |
||
@JsonProperty("results") | ||
private List<RickAndMorty> results; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package mate.academy.rickandmorty.repository; | ||
|
||
import mate.academy.rickandmorty.model.Character; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
import org.springframework.stereotype.Repository; | ||
|
||
@Repository | ||
public interface CharacterRepository extends JpaRepository<Character, Long> { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package mate.academy.rickandmorty.service; | ||
|
||
import java.util.List; | ||
import java.util.Optional; | ||
import mate.academy.rickandmorty.model.Character; | ||
import org.springframework.stereotype.Service; | ||
|
||
@Service | ||
public interface CharacterService { | ||
void fetchCharacters(); | ||
|
||
Optional<Character> getRandomCharacter(); | ||
|
||
List<Character> searchCharactersByName(String word); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package mate.academy.rickandmorty.service.impl; | ||
|
||
import java.util.List; | ||
import java.util.Optional; | ||
import java.util.Random; | ||
import java.util.stream.Collectors; | ||
import lombok.RequiredArgsConstructor; | ||
import mate.academy.rickandmorty.model.Character; | ||
import mate.academy.rickandmorty.model.RickAndMortyResponse; | ||
import mate.academy.rickandmorty.repository.CharacterRepository; | ||
import mate.academy.rickandmorty.service.CharacterService; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.web.client.HttpClientErrorException; | ||
import org.springframework.web.client.RestTemplate; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class CharacterServiceImpl implements CharacterService { | ||
private final RestTemplate restTemplate; | ||
private final CharacterRepository characterRepository; | ||
private final Random random; | ||
|
||
@Override | ||
public void fetchCharacters() { | ||
String url = "https://rickandmortyapi.com/api/character"; | ||
int page = 1; | ||
|
||
while (true) { | ||
String pageUrl = url + "?page=" + page; | ||
RickAndMortyResponse response; | ||
|
||
try { | ||
response = restTemplate.getForObject(pageUrl, RickAndMortyResponse.class); | ||
} catch (HttpClientErrorException e) { | ||
if (e.getStatusCode() == HttpStatus.NOT_FOUND) { | ||
break; | ||
} | ||
throw e; | ||
} | ||
|
||
if (response == null || response.getResults() == null | ||
|| response.getResults().isEmpty()) { | ||
break; | ||
} | ||
|
||
List<Character> characters = response.getResults() | ||
.stream() | ||
.map(result -> { | ||
Character character = new Character(); | ||
character.setExternalId(result.getId()); | ||
|
||
if (result.getName() != null) { | ||
character.setName(result.getName()); | ||
} else { | ||
throw new IllegalArgumentException("Field name is missing"); | ||
} | ||
|
||
character.setGender(result.getGender()); | ||
character.setStatus(result.getStatus()); | ||
return character; | ||
}).collect(Collectors.toList()); | ||
|
||
characterRepository.saveAll(characters); | ||
|
||
page++; | ||
} | ||
} | ||
|
||
@Override | ||
public Optional<Character> getRandomCharacter() { | ||
int randomId = random.nextInt(1, (int) characterRepository.count()); | ||
return characterRepository.findById(Long.valueOf(randomId)); | ||
} | ||
|
||
@Override | ||
public List<Character> searchCharactersByName(String word) { | ||
return characterRepository.findAll().stream() | ||
.filter(ch -> ch.getName().toLowerCase().contains(word.toLowerCase())) | ||
.collect(Collectors.toList()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,7 @@ | ||
spring.datasource.url=jdbc:h2:mem:testdb | ||
spring.datasource.driverClassName=org.h2.Driver | ||
spring.datasource.username=sa | ||
spring.datasource.password=password | ||
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect | ||
spring.h2.console.enabled=true | ||
|
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.
Controllers should work with DTO objects, not with model. Create a response DTO for this endpoint