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

[COT-112] Feature: 랜덤 퀴즈 반환 API 구현 #245

Merged
merged 10 commits into from
Dec 17, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.cotato.csquiz.api.quiz.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.cotato.csquiz.api.quiz.dto.RandomTutorialQuizResponse;
import org.cotato.csquiz.domain.education.service.RandomQuizService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Tag(name = "CS 퀴즈 탭 정보", description = "외부인용 CS 퀴즈 탭 관련 API 입니다.")
@RestController
@RequestMapping("/v2/api/random-quizzes")
@RequiredArgsConstructor
public class RandomQuizController {

private final RandomQuizService randomQuizService;

@Operation(summary = "외부인용 랜덤 퀴즈 반환 API")
@GetMapping
public ResponseEntity<RandomTutorialQuizResponse> pickRandomQuiz() {
return ResponseEntity.ok().body(randomQuizService.pickRandomQuiz());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.cotato.csquiz.api.quiz.dto;

import java.util.List;
import org.cotato.csquiz.domain.education.entity.RandomQuiz;

public record RandomTutorialQuizResponse(
Long id,
String question,
String imageUrl,
List<String> choices
) {
public static RandomTutorialQuizResponse from(final RandomQuiz randomQuiz) {
return new RandomTutorialQuizResponse(
randomQuiz.getId(),
randomQuiz.getQuestion(),
randomQuiz.getImageUrl(),
randomQuiz.getChoices()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ public class SecurityConfig {
"/websocket/csquiz",
"/v2/api/policies",
"/v2/api/events/**",
"/v1/api/generation/current"
"/v1/api/generation/current",
"/v2/api/random-quizzes"
};

private final JwtTokenProvider jwtTokenProvider;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public class JwtAuthorizationFilter extends OncePerRequestFilter {
private static final String PROJECTS_LIST = "/v2/api/projects";
private static final String PROJECT_DETAIL = "/v2/api/projects";
private static final String CURRENT_GENERATION = "/v1/api/generation/current";
private static final String RANDOM_QUIZ_PATH = "/v2/api/random-quizzes";
private static final String INTEGER_REGEX = "/{id:\\d+}";

private final JwtTokenProvider jwtTokenProvider;
Expand Down Expand Up @@ -70,6 +71,7 @@ protected boolean shouldNotFilter(HttpServletRequest request) {
return path.startsWith(AUTH_PATH) || path.equals(LOGIN_PATH)
|| path.startsWith(SWAGGER_PATH) || path.equals(SWAGGER_FAVICON)
|| path.startsWith(SWAGGER_PATH_3) || path.startsWith(WS)
|| path.startsWith(RANDOM_QUIZ_PATH)
|| path.equals(GENERATION_PATH) || path.equals(SESSION_PATH)
|| path.equals(POLICIES_PATH)
|| path.equals(CURRENT_GENERATION)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import java.util.List;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
Expand All @@ -22,4 +23,8 @@ public class Choices {

@Column(nullable = false)
private String fourthChoice;

public List<String> getChoices() {
return List.of(firstChoice, secondChoice, thirdChoice, fourthChoice);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import java.util.List;
import java.util.Objects;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
Expand Down Expand Up @@ -40,4 +42,18 @@ public class RandomQuiz extends BaseTimeEntity {

@Column(name = "answer_number", nullable = false)
private Integer answerNumber;

public List<String> getChoices() {
if (Objects.isNull(choices)) {
throw new IllegalStateException("선택지가 없는 경우가 존재합니다.");
}
return choices.getChoices();
}

public String getImageUrl() {
if (Objects.isNull(image)) {
return null;
}
return image.getUrl();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.cotato.csquiz.domain.education.repository;

import org.cotato.csquiz.domain.education.entity.RandomQuiz;
import org.springframework.data.jpa.repository.JpaRepository;

public interface RandomQuizRepository extends JpaRepository<RandomQuiz, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.cotato.csquiz.domain.education.service;

import lombok.RequiredArgsConstructor;
import org.cotato.csquiz.api.quiz.dto.RandomTutorialQuizResponse;
import org.cotato.csquiz.domain.education.service.component.RandomQuizReader;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class RandomQuizService {

private final RandomQuizReader randomQuizReader;

public RandomTutorialQuizResponse pickRandomQuiz() {
return RandomTutorialQuizResponse.from(randomQuizReader.getRandomQuiz());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.cotato.csquiz.domain.education.service.component;

import jakarta.persistence.EntityNotFoundException;
import java.util.concurrent.ThreadLocalRandom;
import lombok.RequiredArgsConstructor;
import org.cotato.csquiz.domain.education.entity.RandomQuiz;
import org.cotato.csquiz.domain.education.repository.RandomQuizRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class RandomQuizReader {

private final RandomQuizRepository randomQuizRepository;

public RandomQuiz getRandomQuiz() {
final ThreadLocalRandom random = ThreadLocalRandom.current();
int totalCount = (int) randomQuizRepository.count();
if (totalCount == 0) {
throw new EntityNotFoundException("랜덤 퀴즈가 존재하지 않습니다");
}

int randomPage = random.nextInt(totalCount);
Page<RandomQuiz> quizPage = randomQuizRepository.findAll(PageRequest.of(randomPage, 1));
return quizPage.getContent().get(0);
Youthhing marked this conversation as resolved.
Show resolved Hide resolved
}
}
Loading