Solucionado (ver solução)
Solucionado
(ver solução)
2
respostas

Gostaria de uma ajuda de algum professor ou de alguem que possa me ajudar nesse problema que estou tendo em consumir os dados de uma API

Bom, estou criando uma API de um quiz. Estou consumindo os dados de uma API externa que tem perguntas para eu consumir na minha API. Só que na hora de disserializar os dados no record usando a biblioteca Jackson não está funcionando. Os dados vem para API mas nesse momento ele não passa os dados, não sei qual é o problema. A API que estou consumindo é essa: https://opentdb.com/

Vou deixar meu código e os logs aqui abaixo para se alguém poder analisar e me da um caminho para solucionar esse problema.

@Entity
@Table(name = "tb_quiz")
@NoArgsConstructor
@Getter
public class Quiz {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String type;
    private String difficulty;
    private String category;
    private String question;
    private String correctAnswer;
    @OneToMany(mappedBy = "quiz", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
    private List<IncorrectAnswer> incorrectAnswers;

    public Quiz(String type, String difficulty, String category, String question, String correctAnswer) {
        this.type = type;
        this.difficulty = difficulty;
        this.category = category;
        this.question = question;
        this.correctAnswer = correctAnswer;
    }
}
@Entity
@Table(name = "tb_incorrect_answer")
@Getter
@Setter
@NoArgsConstructor
public class IncorrectAnswer {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "quiz_id")
    private Quiz quiz;
}
public record UrlDTO(
        String amount,
        String category,
        String difficulty,
        String type
) {
}
@JsonIgnoreProperties(ignoreUnknown = true)
public record QuizDTO(
        @JsonAlias("type")
        String type,
        @JsonAlias("difficulty")
        String difficulty,
        @JsonAlias("category")
        String category,
        @JsonAlias("question")
        String question,
        @JsonAlias("correct_answer")
        String correctAnswer,
        @JsonAlias("incorrect_answers")
        List<IncorrectAnswer> incorrectAnswers

) {
}
@Service
@Slf4j
public class ConsumeApiImpl implements ConsumeAPI {
    @Override
    public String consumeAPI(String url) {

        if (url == null || url.isEmpty()) {
            throw new IllegalArgumentException("URL is null or empty");
        }
        log.info("Iniciando chamada para a URL: {}", url);

        try {
            URI uri = new URI(url);
            HttpClient client = HttpClient.newHttpClient();
            HttpRequest request = HttpRequest.newBuilder()
                    .uri(uri)
                    .build();
            HttpResponse<String> response = client
                    .send(request, HttpResponse.BodyHandlers.ofString());
            log.info("Resposta recebida da API: {}", response.statusCode());
            return response.body();
        } catch (IOException | InterruptedException | URISyntaxException e) {
            log.error("Erro ao consumir a API: {}", e.getMessage());
            throw new ConsumeApiException("Erro ao consumir a API", e);
        }
    }
}
@Service
@AllArgsConstructor
public class ConvertDataImpl implements ConvertData {

    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public <T> T convertData(String json, Class<T> clazz) {
        try {
            return objectMapper.readValue(json, clazz);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
}
2 respostas
@Service
@AllArgsConstructor
@Slf4j
public class QuizServiceImpl implements QuizService {

    private static final String URL = "https://opentdb.com/api.php?";

    private final QuizRepository quizRepository;
    private final IncorrectAnswerRepository incorrectAnswerRepository;
    private final ConvertData convertData;
    private final ConsumeAPI consumeAPI;

    @Transactional
    @Override
    public void saveQuiz(UrlDTO url) {
        try {
            log.info("Buscando dados da API: {}", url);
            var json = consumeAPI.consumeAPI(URL + "amount=" +url.amount() + "&category="
                    + url.category() + "&difficulty=" + url.difficulty() + "&type=" + url.type());

            var quizDTO = convertData.convertData(json, QuizDTO.class);
            log.info("Convertendo dados para o DTO: {}", quizDTO);

            var quiz = new Quiz();

            List<IncorrectAnswer> incorrectAnswers = quizDTO.incorrectAnswers();
            incorrectAnswers.forEach(incorrectAnswer -> {
                incorrectAnswer.setQuiz(quiz);
                incorrectAnswerRepository.save(incorrectAnswer);
            });
            log.info("Convertendo dados para a entidade: {}", quiz);

            this.quizRepository.save(quiz);
            log.info("Salvando quiz: {}", quiz);
        } catch (Exception e) {
            log.error("Ocorreu um erro ao salvar o quiz: {}", e.getMessage());
            throw new QuizException("Erro ao salvar o quiz", e);
        }
    }
}	
@RestController
@RequestMapping("/quiz")
@AllArgsConstructor
public class QuizController {

    private final QuizService quizService;

    @PostMapping
    public ResponseEntity<Void> saveQuiz(@RequestBody UrlDTO url) {
        this.quizService.saveQuiz(url);
        return new ResponseEntity<>(HttpStatus.CREATED);
    }
}

Insira aqui a descrição dessa imagem para ajudar na acessibilidade

Insira aqui a descrição dessa imagem para ajudar na acessibilidade

Vou deixar uma imagem do codigo debugado aqui:

Insira aqui a descrição dessa imagem para ajudar na acessibilidade

Como vocês podem ver os dados estão chegando mas quando vou passar os dados que deveriam estar no quizDTO para passar no construtor do quiz que seria para salvar no banco os dados de quizDTO vem null. Não sei o motivo que isso está acontecendo. E na parte do quiz não estou passando nd pq estava testando mas o problema é esse que falei que os dados de quizDTO vem nulos

solução!

Consegui solucionar, vou deixar o código corrigido:

@JsonIgnoreProperties(ignoreUnknown = true)
public record QuizApiResponse(
        List<QuizQuestionDTO> results
) {
}
@Transactional
    @Override
    public void saveQuiz(UrlDTO url) {
        try {
            log.info("Iniciando busca de dados da API: {}", url);
            String apiUrl = URL + "amount=" + url.amount() + "&category=" + url.category() + "&difficulty=" + url.difficulty() + "&type=" + url.type();
            log.debug("API URL: {}", apiUrl);

            List<QuizQuestionDTO> questions = quizApiClient.fetchQuizQuestions(apiUrl);
            log.info("Dados da API encontrados com sucesso: {}", questions);

            List<Quiz> quizList = questions.stream().map(quizQuestionDTO -> {
                Quiz quiz = new Quiz();
                quiz.setCategory(quizQuestionDTO.category());
                quiz.setType(quizQuestionDTO.type());
                quiz.setDifficulty(quizQuestionDTO.difficulty());
                quiz.setQuestion(quizQuestionDTO.question());
                quiz.setCorrectAnswer(quizQuestionDTO.correctAnswer());
                quiz.setIncorrectAnswers(quizQuestionDTO.incorrectAnswers().stream().map(incorrectAnswer -> {
                    IncorrectAnswer incorrectAnswerEntity = new IncorrectAnswer();
                    incorrectAnswerEntity.setQuiz(quiz);
                    incorrectAnswerEntity.setIncorrectAnswer(incorrectAnswer);
                    return incorrectAnswerEntity;
                }).collect(Collectors.toList()));
                return quiz;
            }).collect(Collectors.toList());

            log.info("Salvando quiz no banco de dados: {}", quizList);
            quizRepository.saveAll(quizList);
            log.info("Quiz salvo com sucesso.");
        } catch (Exception e) {
            log.error("Erro ao buscar dados da API: {}", e.getMessage());
            throw new QuizException("Erro ao buscar dados da API", e);
        }
    }