[Added] Basis for Backend
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
package com.ninjaws.quiz.Config;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.ninjaws.quiz.Controllers;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.ninjaws.quiz.Models.Session;
|
||||
import com.ninjaws.quiz.Services.QuizService;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/quiz")
|
||||
public class QuizController {
|
||||
|
||||
@Autowired
|
||||
private QuizService quizService;
|
||||
|
||||
@GetMapping("/questions")
|
||||
public String startSession() {
|
||||
// String sessionId = generateSessionId();
|
||||
String sessionId = quizService.createSession();
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
@GetMapping("/questions/{sessionId}")
|
||||
public Optional<Session> checkStatus(@PathVariable String sessionId) {
|
||||
return quizService.checkSession(sessionId);
|
||||
}
|
||||
|
||||
// @PostMapping("/checkanswers")
|
||||
// public Result checkAnswers(@RequestBody String answers) {
|
||||
// return new Result();
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.ninjaws.quiz.Exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
|
||||
/**
|
||||
* Determines behaviour when certain types of Exceptions are encountered
|
||||
*/
|
||||
@ControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
@ExceptionHandler(IllegalArgumentException.class)
|
||||
public ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException e) {
|
||||
return ResponseEntity
|
||||
.status(HttpStatus.BAD_REQUEST)
|
||||
.body(e.getMessage());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.ninjaws.quiz.Models;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
|
||||
/**
|
||||
* The response sent by the external API
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class ApiResponse {
|
||||
@JsonProperty("response_code")
|
||||
private int responseCode;
|
||||
@JsonProperty("results")
|
||||
private List<Question> results;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.ninjaws.quiz.Models;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* Holds the question, answers, and correct answer
|
||||
* TODO: Should not send correct answer to the user
|
||||
*/
|
||||
// @Setter()
|
||||
@Getter()
|
||||
public class Question {
|
||||
private String type;
|
||||
private String difficulty;
|
||||
private String category;
|
||||
private String question;
|
||||
|
||||
// @JsonIgnore
|
||||
private String correctAnswer;
|
||||
|
||||
// @JsonIgnore
|
||||
private List<String> incorrectAnswers;
|
||||
|
||||
@JsonProperty("answers")
|
||||
public List<String> getAnswers() {
|
||||
List<String> possibleAnswers = new ArrayList<>(incorrectAnswers);
|
||||
possibleAnswers.add(correctAnswer);
|
||||
Collections.shuffle(possibleAnswers);
|
||||
return possibleAnswers;
|
||||
}
|
||||
|
||||
@JsonProperty("correct_answer")
|
||||
public void setCorrectAnswer(String correctAnswer) {
|
||||
this.correctAnswer = correctAnswer;
|
||||
}
|
||||
|
||||
@JsonProperty("incorrect_answers")
|
||||
public void setIncorrectAnswer(List<String> incorrectAnswers) {
|
||||
this.incorrectAnswers = incorrectAnswers;
|
||||
}
|
||||
|
||||
@JsonProperty("correct_answer")
|
||||
@JsonIgnore
|
||||
public String getCorrectAnswer() {
|
||||
return correctAnswer;
|
||||
}
|
||||
|
||||
@JsonProperty("incorrect_answers")
|
||||
@JsonIgnore
|
||||
public List<String> getIncorrectAnswers() {
|
||||
return incorrectAnswers;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.ninjaws.quiz.Models;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class Request {
|
||||
private final String sessionId;
|
||||
private final String requestUrl;
|
||||
|
||||
public Request(String sessionId, String requestUrl) {
|
||||
this.sessionId = sessionId;
|
||||
this.requestUrl = requestUrl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.ninjaws.quiz.Models;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class Session {
|
||||
private final String id;
|
||||
private List<Question> questions;
|
||||
|
||||
public Session() {
|
||||
this.id = UUID.randomUUID().toString();
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,10 @@ package com.ninjaws.quiz;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
public class QuizApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.ninjaws.quiz.Services;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@Service
|
||||
public class ApiService {
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
@Autowired
|
||||
public ApiService(RestTemplate restTemplate) {
|
||||
this.restTemplate = restTemplate;
|
||||
}
|
||||
|
||||
public String requestQuestions(String url) {
|
||||
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
|
||||
String jsonData = response.getBody();
|
||||
return jsonData;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package com.ninjaws.quiz.Services;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.ninjaws.quiz.Models.ApiResponse;
|
||||
import com.ninjaws.quiz.Models.Question;
|
||||
import com.ninjaws.quiz.Models.Request;
|
||||
import com.ninjaws.quiz.Models.Session;
|
||||
|
||||
|
||||
@Service
|
||||
public class QuizService {
|
||||
|
||||
@Autowired
|
||||
private ApiService apiService;
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
/**
|
||||
* Stores all active sessions
|
||||
* Gets added to when a user requests data
|
||||
* Gets removed from when a user requests their score (by sending their answers)
|
||||
* TODO: Replace with something that automatically removes items after a certain amount of time (Caffein?)
|
||||
*/
|
||||
private final Map<String,Session> sessions = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Stores all user requests for data
|
||||
* Executes them in order, one every 5 seconds (the limit of the external API)
|
||||
* Removes them from the Queue when the request was succesful
|
||||
*/
|
||||
private final BlockingQueue<Request> requests = new LinkedBlockingQueue<>();
|
||||
|
||||
/**
|
||||
* Actions:
|
||||
* - Creates a session with sessionId
|
||||
* - Adds session to the cache
|
||||
* - Creates request and adds it to the queue
|
||||
* @return The sessionId
|
||||
*/
|
||||
public String createSession() {
|
||||
Session session = new Session();
|
||||
sessions.put(session.getId(), session); // TODO: Either remove id from session, or maybe Caffein does allow them together
|
||||
try {
|
||||
requests.put(new Request(session.getId(), "https://opentdb.com/api.php?amount=10"));
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
||||
return session.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sessionId
|
||||
* Actions:
|
||||
* - Looks up the session in the cache
|
||||
* - Checks if the data from the request is stored
|
||||
* @return The data if it's available, otherwise a statuscode (?)
|
||||
*/
|
||||
public Optional<Session> checkSession(String sessionId) throws IllegalArgumentException {
|
||||
Session session = sessions.get(sessionId);
|
||||
|
||||
if(session == null) {
|
||||
throw new IllegalArgumentException("Invalid session ID: " + sessionId);
|
||||
}
|
||||
|
||||
if (session.getQuestions() == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
System.out.print(session.getQuestions().get(0).getIncorrectAnswers().get(0));
|
||||
return Optional.of(session);
|
||||
}
|
||||
|
||||
/**
|
||||
* Every 5 seconds:
|
||||
* - Take the top request from the queue (removes it)
|
||||
* - Perform the request
|
||||
* - Wait for the data
|
||||
* - Find the matching session from the cache
|
||||
* - Add the data to the session in the cache
|
||||
*/
|
||||
@Scheduled(fixedDelay = 5000)
|
||||
protected void processQueue() {
|
||||
Request request = requests.poll();
|
||||
if (request != null) {
|
||||
String json = apiService.requestQuestions(request.getRequestUrl());
|
||||
try {
|
||||
List<Question> questions = extractQuestions(json);
|
||||
Session session = sessions.get(request.getSessionId());
|
||||
session.setQuestions(questions);
|
||||
sessions.put(request.getSessionId(), session);
|
||||
} catch (IOException e) {
|
||||
//TODO: Log this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<Question> extractQuestions(String json) throws IOException {
|
||||
return objectMapper.readValue(json, ApiResponse.class).getResults();
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives: sessionId and answers
|
||||
* Actions:
|
||||
* - retrieves session from cache
|
||||
* - Deletes session from cache
|
||||
* - Matches answers answers to correctAnswers
|
||||
* - Calculates score
|
||||
* Returns: calculated score
|
||||
* */
|
||||
public void calculateScore() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user