Skip to content
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

init commit #151

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open

init commit #151

wants to merge 4 commits into from

Conversation

TonyH277
Copy link

@TonyH277 TonyH277 commented May 6, 2024

No description provided.

Copy link

@andrii-hoienko andrii-hoienko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job, but some improvements required


}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change


while (url != null) {
String jsonResponse = restTemplate.getForObject(url, String.class);
JsonNode rootNode = objectMapper.readTree(jsonResponse);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the reason to readTree? Is it possible to map to an object?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I choose this aproach because its clear for me to create object from which then I'll fetch info by key, and then manipulate with it. Then I get by key="results" and map to Character.class

private final CharacterRepository characterRepository;

@PostConstruct
public void fetchData() throws JsonProcessingException {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bad method name, I believe fetch data method should return something

private final CharacterRepository characterRepository;

@PostConstruct
public void fetchData() throws JsonProcessingException {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please catch JsonProcessingException and rethrow it as runtime

private final ObjectMapper objectMapper;
private final CharacterRepository characterRepository;

@PostConstruct

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PostConstruct often used for bean configuration, the code that you wrote in not related to bean configuration, so I suggest to use different approach for this, you can use CommandLineRunner interface(or bean) or use event listeners to run code on startup


@Override
public Character generateRandomCharacter() {
return Optional.of(characterRepository.findById(new Random().nextLong(CHARACTERS_AMOUNT)))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you really want to create Random object each time this method is called

@Service
@RequiredArgsConstructor
public class CharacterServiceImpl implements CharacterService {
private static final int CHARACTERS_AMOUNT = 826;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if some characters will be added or removed?

import java.io.IOException;
import mate.academy.rickandmorty.model.Character;

public class CharacterDeserializer extends JsonDeserializer<Character> {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is redundant, object mapper could deserialize your objects without additional configuration

Copy link
Author

@TonyH277 TonyH277 May 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does, but it maps id from Json to Entity.id. I thought JsonProperty annotation will solve this problem, but had mapping exception with msg like (I want to set json.id to entity.id, but your annotation said that i should set json.id to other field)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is why you need to create an externalDto to map response from API and then remap it to entity and save it, such approach will be more flexible because we have a separate class to represent characters in external API. In general better to avoid custom deserialization logic, because it is often confusing. Flow when we map external response as is and then work with it as we want is preferable, I could describe in more details on QnA

Comment on lines 4 to 5
spring.datasource.username=root
spring.datasource.password=root

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don`t push createntials

@RequiredArgsConstructor
public class DataFetcher {

private static final String CHARACTER_URL = "https://rickandmortyapi.com/api/character";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to make this url configurable via propertioes

Copy link

@andrii-hoienko andrii-hoienko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, left few comments

Comment on lines 24 to 27
return Optional.of(characterRepository.findById(id))
.get()
.orElseThrow(() -> new IllegalStateException("Character not found with id: " + id));
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as far as I remember characterRepository.findById(id) returns optional. So you make Optional.of(Optional returned from repository).get (//Unboxing optional that you create) .orElseThrow (//Working with optional returned from repo)

import java.io.IOException;
import mate.academy.rickandmorty.model.Character;

public class CharacterDeserializer extends JsonDeserializer<Character> {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is why you need to create an externalDto to map response from API and then remap it to entity and save it, such approach will be more flexible because we have a separate class to represent characters in external API. In general better to avoid custom deserialization logic, because it is often confusing. Flow when we map external response as is and then work with it as we want is preferable, I could describe in more details on QnA

try {
JsonNode rootNode = objectMapper.readTree(jsonResponse);
List<Character> pageCharacters = deserializeCharacters(rootNode);
characterRepository.saveAll(pageCharacters);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest to fetch all characters first and than save them all to not make request to db several times


@Component
@RequiredArgsConstructor
public class DataFetcher {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public class DataFetcher {
public class DataFetchService{

@Value("${characters.url}")
private String characterUrl;

public void populateCharactersDB() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public void populateCharactersDB() {
public List<Characters> fetch() {

Comment on lines 9 to 18
@Component
@RequiredArgsConstructor
public class CustomApplicationRunner implements ApplicationRunner {
private final DataFetcher dataFetcher;

@Override
public void run(ApplicationArguments args) {
dataFetcher.populateCharactersDB();
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@Component
@RequiredArgsConstructor
public class CustomApplicationRunner implements ApplicationRunner {
private final DataFetcher dataFetcher;
@Override
public void run(ApplicationArguments args) {
dataFetcher.populateCharactersDB();
}
}
@Component
@RequiredArgsConstructor
public class DataPopulator implements ApplicationRunner {//Not best name, you can think about better one
private final DataFetcher dataFetcher;
private final Repo repo
@Override
public void run(ApplicationArguments args) {
characters = dataFetcher.populateCharactersDB();
repo.saveAll(characters)
}
}

Copy link

@andrii-hoienko andrii-hoienko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants