Added Project
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
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) {
|
||||
SpringApplication.run(QuizApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.ninjaws.quiz.components;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.ninjaws.quiz.services.ApiService;
|
||||
import com.ninjaws.quiz.services.StateManagementService;
|
||||
|
||||
|
||||
@Component
|
||||
public class StartupRunner implements CommandLineRunner {
|
||||
|
||||
@Autowired
|
||||
private StateManagementService stateManagementService;
|
||||
@Autowired
|
||||
private ApiService apiService;
|
||||
|
||||
@Override
|
||||
public void run(String... args) {
|
||||
|
||||
/**
|
||||
* On startup:
|
||||
* - Set ProcessingState to Startup
|
||||
* - Store the categories
|
||||
* - Get a sessiontoken
|
||||
*
|
||||
* * Inside the SchedulerService
|
||||
* - Start filling the question table
|
||||
*
|
||||
* * Inside the Response Handling Service
|
||||
* - No more questions? Stop
|
||||
* - Set ProcessingState to Normal
|
||||
*/
|
||||
|
||||
stateManagementService.setCurrentProcessingState(StateManagementService.ProcessingState.STARTUP);
|
||||
apiService.getCategories();
|
||||
apiService.getSessionToken();
|
||||
}
|
||||
}
|
||||
@@ -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,20 @@
|
||||
package com.ninjaws.quiz.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
@EnableWebMvc
|
||||
public class WebConfig implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**") // Allow all endpoints
|
||||
.allowedOrigins("http://localhost:4200", "https://quiz.ianvink.nl") // Allow specific origin
|
||||
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // Allow specific methods
|
||||
.allowedHeaders("*") // Allow all headers
|
||||
.allowCredentials(true); // Allow credentials
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.ninjaws.quiz.controllers;
|
||||
|
||||
import java.util.List;
|
||||
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.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.ninjaws.quiz.entities.Category;
|
||||
import com.ninjaws.quiz.models.Answers;
|
||||
import com.ninjaws.quiz.models.QuizSettings;
|
||||
import com.ninjaws.quiz.models.Session;
|
||||
import com.ninjaws.quiz.services.DataService;
|
||||
import com.ninjaws.quiz.services.QuizService;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/quiz")
|
||||
public class QuizController {
|
||||
|
||||
@Autowired
|
||||
private QuizService quizService;
|
||||
@Autowired
|
||||
private DataService dataService;
|
||||
|
||||
|
||||
/**
|
||||
* Start a session
|
||||
* @param amount The amount of questsions
|
||||
* @param category The desired category (or all)
|
||||
* @param difficulty The desired difficulty (or all)
|
||||
* @param type The desired type (or both)
|
||||
* @return The sessionId, or statusCode 5 when the queue is full
|
||||
*/
|
||||
@GetMapping("/questions")
|
||||
public String startSession(
|
||||
@RequestParam(required = true) Integer amount,
|
||||
@RequestParam(required = false) Integer category,
|
||||
@RequestParam(required = false) String difficulty,
|
||||
@RequestParam(required = false) String type
|
||||
) {
|
||||
QuizSettings quizSettings = new QuizSettings(amount, category, difficulty, type);
|
||||
Optional<String> result = quizService.createSession(quizSettings);
|
||||
if (result.isPresent()) {
|
||||
String jsonString = String.format("{\"statusCode\":0,\"sessionId\":\"%s\"}", result.get());
|
||||
return jsonString;
|
||||
}
|
||||
String jsonString = String.format("{\"statusCode\":5}");
|
||||
return jsonString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the questions have been retrieved
|
||||
* @param sessionId The session that holds the questions
|
||||
* @return The Session, or nothing if they are not ready yet
|
||||
*/
|
||||
@GetMapping("/status/{sessionId}")
|
||||
public Optional<Session> checkStatus(@PathVariable String sessionId) {
|
||||
return quizService.checkSession(sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive the chosen answers, calculate the score, and return it
|
||||
* @param answers The chosen answers
|
||||
* @return The score
|
||||
*/
|
||||
@PostMapping("/score")
|
||||
public String checkAnswers(@RequestBody Answers answers) {
|
||||
String jsonString = String.format("{\"score\":\"%s\"}", quizService.getScore(answers));
|
||||
return jsonString;
|
||||
}
|
||||
|
||||
@GetMapping("/categories")
|
||||
public List<Category> getCategories(){
|
||||
return dataService.getCategories();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.ninjaws.quiz.entities;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@MappedSuperclass
|
||||
@Getter
|
||||
@Setter
|
||||
public class BaseQuestionEntity {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
private UUID id;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "category", nullable = false)
|
||||
private Category category;
|
||||
|
||||
private String difficulty;
|
||||
private String type;
|
||||
private String question;
|
||||
|
||||
private String correctAnswer;
|
||||
// @ElementCollection // This stores it in a separate table, it's just a set of strings so not required right now
|
||||
private List<String> incorrectAnswers;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.ninjaws.quiz.entities;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Entity
|
||||
@Table(name = "category")
|
||||
@Getter
|
||||
@Setter
|
||||
public class Category {
|
||||
@Id
|
||||
private Integer id;
|
||||
private String name;
|
||||
|
||||
public Category() {}
|
||||
|
||||
public Category(int id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.ninjaws.quiz.entities;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "question_collecting")
|
||||
public class QuestionCollectingEntity extends BaseQuestionEntity{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.ninjaws.quiz.entities;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "question")
|
||||
public class QuestionEntity extends BaseQuestionEntity {
|
||||
|
||||
}
|
||||
@@ -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,19 @@
|
||||
package com.ninjaws.quiz.models;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class Answers {
|
||||
private String sessionId;
|
||||
private List<String> answers;
|
||||
|
||||
public Answers() {}
|
||||
|
||||
public Answers(String sessionId) {
|
||||
this.sessionId = sessionId;
|
||||
}
|
||||
}
|
||||
@@ -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<QuestionDTO> results;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.ninjaws.quiz.models;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.ninjaws.quiz.entities.Category;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class CategoryResponse {
|
||||
List<Category> trivia_categories;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
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;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class QuestionDTO {
|
||||
|
||||
private String category;
|
||||
private String difficulty;
|
||||
private String type;
|
||||
private String question;
|
||||
|
||||
//* Hidden from the user */
|
||||
private String correctAnswer;
|
||||
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,62 @@
|
||||
package com.ninjaws.quiz.models;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class QuizSettings {
|
||||
|
||||
@Min(1)
|
||||
@Max(50)
|
||||
private int amount = 10;
|
||||
private Integer category;
|
||||
private String difficulty;
|
||||
private String type;
|
||||
|
||||
public QuizSettings(){}
|
||||
|
||||
/** Inverse of toApiQuery */
|
||||
public QuizSettings(int amount, Integer category, String difficulty, String type) {
|
||||
this.amount = amount;
|
||||
this.category = category;
|
||||
this.difficulty = difficulty;
|
||||
this.type = type;
|
||||
// this.category = category == null ? Category.ANY : Category.values()[category];
|
||||
// this.difficulty = difficulty == null ? Difficulty.ANY : Difficulty.valueOf(difficulty.toUpperCase());
|
||||
// this.type = type == null ? Type.ANY : type.equals("boolean") ? Type.BOOLEAN : Type.MULTIPLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the value is between 1 and 50
|
||||
* @Valid doesn't seem to be enforcing it, so this ensures the value is legal
|
||||
*/
|
||||
public void setAmount(@Valid int amount) {
|
||||
this.amount = Math.max(1, Math.min(amount, 50));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the class to a format that can be provided as arguments for the api request
|
||||
* @return The resulting request arguments as String
|
||||
*/
|
||||
public String toApiQuery() {
|
||||
StringBuilder query = new StringBuilder("?amount=" + amount);
|
||||
|
||||
if (category != null) {
|
||||
query.append("&category=").append(category);
|
||||
}
|
||||
|
||||
if (difficulty != null ) {
|
||||
query.append("&difficulty=").append(difficulty);
|
||||
}
|
||||
|
||||
if (type != null ) {
|
||||
query.append("&type=").append(type);
|
||||
}
|
||||
|
||||
return query.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.ninjaws.quiz.models;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class Request {
|
||||
private final String sessionId;
|
||||
private final QuizSettings quizSettings;
|
||||
|
||||
public Request(String sessionId, QuizSettings quizSettings) {
|
||||
this.sessionId = sessionId;
|
||||
this.quizSettings = quizSettings;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
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 int statusCode = 0;
|
||||
private List<QuestionDTO> questions;
|
||||
|
||||
public Session() {
|
||||
this.id = UUID.randomUUID().toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.ninjaws.quiz.models;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class SessionTokenDTO {
|
||||
@JsonProperty("response_code")
|
||||
private int responseCode;
|
||||
@JsonProperty("token")
|
||||
private String token;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.ninjaws.quiz.repositories;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import com.ninjaws.quiz.entities.Category;
|
||||
|
||||
public interface CategoryRepository extends JpaRepository<Category, Long>{
|
||||
Optional<Category> findByName(String name);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.ninjaws.quiz.repositories;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import com.ninjaws.quiz.entities.QuestionCollectingEntity;
|
||||
|
||||
/**
|
||||
* This table is used for collecting questions from the external API
|
||||
* When it finishes, the data from this repository will replace the data in the QuestionRepository, to ensure it stays fresh
|
||||
* This should be scheduled once every day
|
||||
*/
|
||||
public interface QuestionCollectingRepository extends JpaRepository<QuestionCollectingEntity, Long>{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.ninjaws.quiz.repositories;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
import com.ninjaws.quiz.entities.QuestionEntity;
|
||||
|
||||
import jakarta.transaction.Transactional;
|
||||
|
||||
public interface QuestionRepository extends JpaRepository<QuestionEntity, Long>{//, JpaSpecificationExecutor<Question> {
|
||||
|
||||
// @Query(value = "SELECT * FROM question AS q " +
|
||||
// "WHERE (?1 IS NULL OR q.category = ?1) " +
|
||||
// "AND (?2 IS NULL OR q.difficulty = ?2) " +
|
||||
// "AND (?3 IS NULL OR q.type = ?3) " +
|
||||
// "ORDER BY RAND() LIMIT ?4", nativeQuery = true)
|
||||
@Query(value = "SELECT * FROM question AS q " +
|
||||
"WHERE (:categoryId IS NULL OR q.category = :categoryId) " +
|
||||
"AND (:difficulty IS NULL OR q.difficulty = :difficulty) " +
|
||||
"AND (:type IS NULL OR q.type = :type) " +
|
||||
"ORDER BY RAND() LIMIT :limit;", nativeQuery = true)
|
||||
List<QuestionEntity> lookupQuestions(
|
||||
@Param("categoryId") Integer categoryId,
|
||||
@Param("difficulty") String difficulty,
|
||||
@Param("type") String type,
|
||||
@Param("limit") int limit);
|
||||
|
||||
/**
|
||||
* Moves all data from one table to another
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Query(value="INSERT INTO question SELECT * FROM question_collecting;", nativeQuery=true)
|
||||
void moveCollection();
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package com.ninjaws.quiz.services;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestClientException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.ninjaws.quiz.models.ApiResponse;
|
||||
import com.ninjaws.quiz.models.CategoryResponse;
|
||||
import com.ninjaws.quiz.models.QuizSettings;
|
||||
import com.ninjaws.quiz.models.Request;
|
||||
import com.ninjaws.quiz.models.SessionTokenDTO;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@Service
|
||||
public class ApiService {
|
||||
|
||||
@Autowired
|
||||
private CleanerService cleanerService;
|
||||
@Autowired
|
||||
private ResponseHandlingService responseHandler;
|
||||
@Autowired
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
private static final System.Logger logger = System.getLogger(ApiService.class.getName());
|
||||
|
||||
private final String apiUrl = "https://opentdb.com";
|
||||
private String sessionToken;
|
||||
|
||||
public ApiService() {
|
||||
}
|
||||
|
||||
private String buildRequestString(QuizSettings quizSettings) {
|
||||
return apiUrl + "/api.php" + quizSettings.toApiQuery();
|
||||
}
|
||||
|
||||
public void handleRequest(@Valid Request request) {
|
||||
String url = buildRequestString(request.getQuizSettings());
|
||||
|
||||
try {
|
||||
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
|
||||
String json = response.getBody();
|
||||
ApiResponse apiResponse = cleanerService.extractApiResponse(json);
|
||||
responseHandler.handleResponse(request.getSessionId(), apiResponse);
|
||||
|
||||
} catch (RestClientException | IOException | IllegalStateException e) {
|
||||
logger.log(System.Logger.Level.ERROR, "Critical failure", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect a chuck of 50 items
|
||||
* Store them in the DataService
|
||||
*/
|
||||
public void collectBulk() {
|
||||
String tokenStr = sessionToken == null ? "" : "&token="+sessionToken;
|
||||
String url = apiUrl + "/api.php?amount=50" + tokenStr;
|
||||
try {
|
||||
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
|
||||
String json = response.getBody();
|
||||
ApiResponse apiResponse = cleanerService.extractApiResponse(json);
|
||||
responseHandler.handleResponse(apiResponse);
|
||||
|
||||
/**
|
||||
* A bit dirty, but otherwise it causes circular dependencies
|
||||
* Makes sure the categories are refreshed after finishing to replace the questions, to make sure the ids match
|
||||
* And resets the sessionToken, it will be remade during the next REFRESH
|
||||
*/
|
||||
if(apiResponse.getResponseCode() == 4) {
|
||||
getCategories();
|
||||
sessionToken = null;
|
||||
}
|
||||
|
||||
} catch (RestClientException | IOException e) {
|
||||
logger.log(System.Logger.Level.ERROR, "Critical failure", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void getSessionToken() {
|
||||
String url = apiUrl + "/api_token.php?command=request";
|
||||
try {
|
||||
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
|
||||
String json = response.getBody();
|
||||
SessionTokenDTO tokenDto = cleanerService.extractSessionToken(json);
|
||||
sessionToken = tokenDto.getToken();
|
||||
|
||||
} catch (RestClientException | IOException e) {
|
||||
logger.log(System.Logger.Level.ERROR, "Critical failure", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void getCategories() {
|
||||
String url = apiUrl + "/api_category.php";
|
||||
try {
|
||||
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
|
||||
String json = response.getBody();
|
||||
CategoryResponse categoryResponse = cleanerService.extractCategoryResponse(json);
|
||||
responseHandler.handleCategoryResponse(categoryResponse);
|
||||
|
||||
} catch (RestClientException | IOException e) {
|
||||
logger.log(System.Logger.Level.ERROR, "Critical failure", e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.ninjaws.quiz.services;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.ninjaws.quiz.models.Session;
|
||||
|
||||
@Service
|
||||
public class CacheService {
|
||||
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
private final Cache<String,Session> sessions;
|
||||
|
||||
public CacheService() {
|
||||
sessions = (Caffeine.newBuilder()
|
||||
.expireAfterWrite(1,TimeUnit.HOURS)
|
||||
.build());
|
||||
}
|
||||
|
||||
public void addToCache(String key, Session session) {
|
||||
sessions.put(key, session);
|
||||
}
|
||||
|
||||
public Session getFromCache(String sessionId) {
|
||||
return sessions.getIfPresent(sessionId);
|
||||
}
|
||||
|
||||
public void removeFromCache(String sessionId) {
|
||||
sessions.invalidate(sessionId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package com.ninjaws.quiz.services;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.ninjaws.quiz.entities.Category;
|
||||
import com.ninjaws.quiz.entities.QuestionCollectingEntity;
|
||||
import com.ninjaws.quiz.entities.QuestionEntity;
|
||||
import com.ninjaws.quiz.models.ApiResponse;
|
||||
import com.ninjaws.quiz.models.CategoryResponse;
|
||||
import com.ninjaws.quiz.models.QuestionDTO;
|
||||
import com.ninjaws.quiz.models.SessionTokenDTO;
|
||||
import com.ninjaws.quiz.repositories.CategoryRepository;
|
||||
|
||||
/**
|
||||
* Deals with conversions from one form of data to another
|
||||
* Json to Object
|
||||
* Entity to DTO
|
||||
* etc
|
||||
*/
|
||||
@Service
|
||||
public class CleanerService {
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
@Autowired
|
||||
private CategoryRepository categoryRepository;
|
||||
|
||||
public CleanerService() {
|
||||
}
|
||||
|
||||
public ApiResponse extractApiResponse(String json) throws IOException {
|
||||
return objectMapper.readValue(json, ApiResponse.class);
|
||||
}
|
||||
|
||||
public CategoryResponse extractCategoryResponse(String json) throws IOException {
|
||||
return objectMapper.readValue(json, CategoryResponse.class);
|
||||
}
|
||||
|
||||
public List<QuestionDTO> questionListEntitytoDTO(List<QuestionEntity> entities) {
|
||||
List<QuestionDTO> dtos = new ArrayList<>();
|
||||
for (int i = 0; i < entities.size(); i++) {
|
||||
QuestionEntity entity = entities.get(i);
|
||||
dtos.add(questionEntityToDTO(entity));
|
||||
}
|
||||
return dtos;
|
||||
}
|
||||
|
||||
public SessionTokenDTO extractSessionToken(String json) throws IOException {
|
||||
return objectMapper.readValue(json, SessionTokenDTO.class);
|
||||
}
|
||||
|
||||
public QuestionDTO questionEntityToDTO(QuestionEntity entity) {
|
||||
QuestionDTO dto = new QuestionDTO();
|
||||
dto.setQuestion(entity.getQuestion());
|
||||
dto.setCorrectAnswer(entity.getCorrectAnswer());
|
||||
dto.setIncorrectAnswer(entity.getIncorrectAnswers());
|
||||
dto.setCategory(entity.getCategory().getName());
|
||||
dto.setType(entity.getType());
|
||||
dto.setDifficulty(entity.getDifficulty());
|
||||
return dto;
|
||||
}
|
||||
|
||||
public List<QuestionEntity> questionListDTOtoEntity(List<QuestionDTO> dtos) {
|
||||
List<QuestionEntity> entities = new ArrayList<>();
|
||||
for (int i = 0; i < dtos.size(); i++) {
|
||||
QuestionDTO dto = dtos.get(i);
|
||||
entities.add(questionDTOtoEntity(dto));
|
||||
}
|
||||
return entities;
|
||||
}
|
||||
|
||||
public QuestionEntity questionDTOtoEntity(QuestionDTO dto) {
|
||||
QuestionEntity entity = new QuestionEntity();
|
||||
Category category = categoryRepository.findByName(decodeString(dto.getCategory())).orElseThrow(() -> new IllegalStateException("Category not in DB!"));
|
||||
entity.setCategory(category);
|
||||
entity.setDifficulty(dto.getDifficulty());
|
||||
entity.setType(dto.getType());
|
||||
entity.setQuestion(dto.getQuestion());
|
||||
entity.setCorrectAnswer(dto.getCorrectAnswer());
|
||||
entity.setIncorrectAnswers(dto.getIncorrectAnswers());
|
||||
return entity;
|
||||
}
|
||||
|
||||
|
||||
// TODO: Find a simpler way to convert from one table to another if the data is identical so this nonsense isn't necessary
|
||||
public List<QuestionCollectingEntity> questionListDTOtoCollectingEntity(List<QuestionDTO> dtos) {
|
||||
List<QuestionCollectingEntity> entities = new ArrayList<>();
|
||||
for (int i = 0; i < dtos.size(); i++) {
|
||||
QuestionDTO dto = dtos.get(i);
|
||||
entities.add(questionDTOtoCollectingEntity(dto));
|
||||
}
|
||||
return entities;
|
||||
}
|
||||
|
||||
public QuestionCollectingEntity questionDTOtoCollectingEntity(QuestionDTO dto) {
|
||||
QuestionCollectingEntity entity = new QuestionCollectingEntity();
|
||||
Category category = categoryRepository.findByName(decodeString(dto.getCategory())).orElseThrow(() -> new IllegalStateException("Category not in DB!"));
|
||||
entity.setCategory(category);
|
||||
entity.setDifficulty(dto.getDifficulty());
|
||||
entity.setType(dto.getType());
|
||||
entity.setQuestion(dto.getQuestion());
|
||||
entity.setCorrectAnswer(dto.getCorrectAnswer());
|
||||
entity.setIncorrectAnswers(dto.getIncorrectAnswers());
|
||||
return entity;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* For some reason, the categories come with special characters,
|
||||
* But the categories from the questions dont
|
||||
* So this has to be manually fixed if they are to be stored in the db (otherwise namematching doesnt work)
|
||||
*/
|
||||
private String decodeString(String text){
|
||||
return StringEscapeUtils.unescapeHtml4(text);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.ninjaws.quiz.services;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.ninjaws.quiz.entities.Category;
|
||||
import com.ninjaws.quiz.entities.QuestionCollectingEntity;
|
||||
import com.ninjaws.quiz.entities.QuestionEntity;
|
||||
import com.ninjaws.quiz.models.QuizSettings;
|
||||
import com.ninjaws.quiz.repositories.CategoryRepository;
|
||||
import com.ninjaws.quiz.repositories.QuestionCollectingRepository;
|
||||
import com.ninjaws.quiz.repositories.QuestionRepository;
|
||||
|
||||
import jakarta.transaction.Transactional;
|
||||
|
||||
@Service
|
||||
public class DataService {
|
||||
|
||||
@Autowired
|
||||
private QuestionRepository questionRepository;
|
||||
@Autowired
|
||||
private QuestionCollectingRepository collectingRepository;
|
||||
@Autowired
|
||||
private CategoryRepository categoryRepository;
|
||||
|
||||
public List<QuestionEntity> getAllQuestions() {
|
||||
return questionRepository.findAll();
|
||||
}
|
||||
|
||||
public QuestionEntity saveQuestion(QuestionEntity question) {
|
||||
return questionRepository.save(question);
|
||||
}
|
||||
|
||||
public List<QuestionEntity> saveQuestions(List<QuestionEntity> questions) {
|
||||
return questionRepository.saveAll(questions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to get questions that match the specified QuizSettings
|
||||
*
|
||||
* @return The questions if the query was possible, and an empty array if not
|
||||
*/
|
||||
public List<QuestionEntity> lookupQuestions(QuizSettings quizSettings) {
|
||||
return questionRepository.lookupQuestions(quizSettings.getCategory(), quizSettings.getDifficulty(), quizSettings.getType(), quizSettings.getAmount());
|
||||
}
|
||||
|
||||
public List<Category> saveCategories(List<Category> categories) {
|
||||
return categoryRepository.saveAll(categories);
|
||||
}
|
||||
|
||||
public List<Category> getCategories(){
|
||||
return categoryRepository.findAll();
|
||||
}
|
||||
/**
|
||||
* Moving the collected questions to the questions list
|
||||
*
|
||||
*/
|
||||
@Transactional
|
||||
public void migrateQuestions() {
|
||||
questionRepository.deleteAll();
|
||||
questionRepository.moveCollection();
|
||||
collectingRepository.deleteAll();
|
||||
}
|
||||
|
||||
public List<QuestionCollectingEntity> collectQuestions(List<QuestionCollectingEntity> questions) {
|
||||
return collectingRepository.saveAll(questions);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.ninjaws.quiz.services;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.ninjaws.quiz.models.Request;
|
||||
|
||||
@Service
|
||||
public class QueueService {
|
||||
|
||||
/**
|
||||
* 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<>(5);
|
||||
|
||||
/**
|
||||
* Offer automatically doesn't add it to the queue if it is full, so no Exceptions thrown
|
||||
* @return Whether it's possible to add the request to the queue
|
||||
*/
|
||||
public boolean addToQueue(Request request) {
|
||||
return requests.offer(request);
|
||||
}
|
||||
|
||||
public Request popFromQueue() {
|
||||
return requests.poll();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package com.ninjaws.quiz.services;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.ninjaws.quiz.entities.QuestionEntity;
|
||||
import com.ninjaws.quiz.models.Answers;
|
||||
import com.ninjaws.quiz.models.QuizSettings;
|
||||
import com.ninjaws.quiz.models.Request;
|
||||
import com.ninjaws.quiz.models.Session;
|
||||
|
||||
|
||||
@Service
|
||||
public class QuizService {
|
||||
|
||||
@Autowired
|
||||
private CacheService cacheService;
|
||||
@Autowired
|
||||
private QueueService queueService;
|
||||
@Autowired
|
||||
private DataService dataService;
|
||||
@Autowired
|
||||
private CleanerService cleanerService;
|
||||
@Autowired
|
||||
private StateManagementService stateManager;
|
||||
|
||||
/**
|
||||
* Actions:
|
||||
* - Creates a session with sessionId
|
||||
* - Adds session to the cache
|
||||
* - Creates a request and adds it to the queue
|
||||
* @param quizSettings The values set by the user, determining the kind of quiz they want
|
||||
* @return The sessionId
|
||||
*/
|
||||
public Optional<String> createSession(QuizSettings quizSettings) {
|
||||
Session session = new Session();
|
||||
|
||||
/** First, we check if there are already enough items in our db */
|
||||
List<QuestionEntity> questionsRetrieved = dataService.lookupQuestions(quizSettings);
|
||||
/** If the items are not matching the required amount, and the db is not currently being filled up, then they simply don't exist */
|
||||
if (questionsRetrieved.size() < quizSettings.getAmount() && stateManager.getCurrentProcessingState() != StateManagementService.ProcessingState.STARTUP) {
|
||||
session.setStatusCode(1);
|
||||
cacheService.addToCache(session.getId(), session);
|
||||
return Optional.of(session.getId());
|
||||
}
|
||||
/** All items are collected in from the database, it doesn't need to be added to the queue */
|
||||
if(!questionsRetrieved.isEmpty() && questionsRetrieved.size() == quizSettings.getAmount()) {
|
||||
session.setQuestions(cleanerService.questionListEntitytoDTO(questionsRetrieved));
|
||||
cacheService.addToCache(session.getId(), session);
|
||||
return Optional.of(session.getId());
|
||||
}
|
||||
|
||||
cacheService.addToCache(session.getId(), session);
|
||||
boolean addedToQueue = queueService.addToQueue(new Request(session.getId(), quizSettings));
|
||||
/** In this order, because doing the queue before the cache could lead to race conditions (request completion immediately calls cache, but then the item doesn't exist) */
|
||||
if(!addedToQueue) {
|
||||
cacheService.removeFromCache(session.getId());
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(session.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions:
|
||||
* - Looks up the session in the cache
|
||||
* - Checks if the data from the request is stored
|
||||
* @param sessionId
|
||||
* @return The data if it's available, Optional.empty() if not, and an Exception if the session is invalid
|
||||
*/
|
||||
public Optional<Session> checkSession(String sessionId) throws IllegalArgumentException {
|
||||
Session session = cacheService.getFromCache(sessionId);
|
||||
|
||||
if(session == null) {
|
||||
throw new IllegalArgumentException("Invalid session ID: " + sessionId);
|
||||
}
|
||||
|
||||
/** Anything other than statuscode 0 doesn't result in active sessions, so remove them from the cache after reporting to the frontend */
|
||||
if(session.getStatusCode() != 0) {
|
||||
cacheService.removeFromCache(session.getId());
|
||||
return Optional.of(session);
|
||||
}
|
||||
|
||||
/** Still waiting for a response */
|
||||
if (session.getQuestions() == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
|
||||
return Optional.of(session);
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives: sessionId and answers
|
||||
*
|
||||
* Actions:
|
||||
* - Retrieves session from cache
|
||||
* - Deletes session from cache
|
||||
* - Matches answers answers to correctAnswers
|
||||
* - Calculates score
|
||||
* Returns: calculated score
|
||||
* */
|
||||
public int getScore(Answers answers) {
|
||||
Session session = cacheService.getFromCache(answers.getSessionId());
|
||||
cacheService.removeFromCache(session.getId());
|
||||
return calculateScore(session, answers);
|
||||
}
|
||||
|
||||
private int calculateScore(Session session, Answers answers) {
|
||||
int amountCorrect = 0;
|
||||
for(int i = 0; i < session.getQuestions().size(); i++) {
|
||||
String correctAnswer = session.getQuestions().get(i).getCorrectAnswer();
|
||||
String userAnswer = answers.getAnswers().get(i);
|
||||
if (correctAnswer.equalsIgnoreCase(userAnswer)) {
|
||||
amountCorrect++;
|
||||
}
|
||||
}
|
||||
return amountCorrect;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package com.ninjaws.quiz.services;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.ninjaws.quiz.models.ApiResponse;
|
||||
import com.ninjaws.quiz.models.CategoryResponse;
|
||||
import com.ninjaws.quiz.models.Session;
|
||||
|
||||
@Service
|
||||
public class ResponseHandlingService {
|
||||
|
||||
@Autowired
|
||||
private StateManagementService stateManager;
|
||||
@Autowired
|
||||
private CacheService cacheService;
|
||||
@Autowired
|
||||
private DataService dataService;
|
||||
@Autowired
|
||||
private CleanerService cleanerService;
|
||||
|
||||
/**
|
||||
* If there is no sessionId, then this is a request from the scheduler
|
||||
* purely to stock in the database No Cache call is required
|
||||
*/
|
||||
public void handleResponse(String sessionId, ApiResponse response) throws IllegalStateException {
|
||||
|
||||
Session session = cacheService.getFromCache(sessionId);
|
||||
if (session == null) {
|
||||
// Critical system failure: Should never occur
|
||||
throw new IllegalStateException("Session should be defined!");
|
||||
}
|
||||
|
||||
switch (response.getResponseCode()) {
|
||||
case 0:
|
||||
// Success
|
||||
updateSessionInCache(session, response);
|
||||
/** Managing the token gets difficult since there is a timelimit, so simply dont add these, let the bulk collector do it */
|
||||
// dataService.saveQuestions(cleanerService.questionListDTOtoEntity(response.getResults()));
|
||||
break;
|
||||
case 1:
|
||||
// Not enough questions to satisfy the request.
|
||||
updateSessionInCache(session, response);
|
||||
break;
|
||||
case 2:
|
||||
// Invalid parameters
|
||||
updateSessionInCache(session, response);
|
||||
break;
|
||||
case 3:
|
||||
// If it expired. Shouldn't happen since the backup database is full within 40 minutes, and the code lasts for 6 hours
|
||||
// Return a statuscode 1 to signal an issue, just in case a user hits this situation (should not happen)
|
||||
session.setStatusCode(1);
|
||||
updateSessionInCache(session, response);
|
||||
case 4:
|
||||
// It returns nothing, the previous batch was the last one
|
||||
// Return a statuscode 1 to the user
|
||||
session.setStatusCode(1);
|
||||
updateSessionInCache(session, response);
|
||||
break;
|
||||
case 5:
|
||||
// Should be prevented by the scheduler
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the request was made by the scheduler during queue-downtime, there is no session
|
||||
*/
|
||||
public void handleResponse(ApiResponse response) {
|
||||
switch (response.getResponseCode()) {
|
||||
case 0:
|
||||
if(stateManager.getCurrentProcessingState() == StateManagementService.ProcessingState.STARTUP) {
|
||||
/** Initial saving during the startup */
|
||||
dataService.saveQuestions(cleanerService.questionListDTOtoEntity(response.getResults()));
|
||||
} else if (stateManager.getCurrentProcessingState() == StateManagementService.ProcessingState.REFRESH) {
|
||||
/** Background collecting during the night */
|
||||
dataService.collectQuestions(cleanerService.questionListDTOtoCollectingEntity(response.getResults()));
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
// Signal DataService that we have extracted all the data. Stop gathering,
|
||||
// TODO: Switch to caching-mode
|
||||
|
||||
/**
|
||||
* If it was in the REFRESH state, move the data from collecting to the used questions
|
||||
* Otherwise simply switch to the NORMAL state
|
||||
*/
|
||||
if (stateManager.getCurrentProcessingState() == StateManagementService.ProcessingState.REFRESH) {
|
||||
dataService.migrateQuestions();
|
||||
}
|
||||
stateManager.setCurrentProcessingState(StateManagementService.ProcessingState.NORMAL);
|
||||
break;
|
||||
case 5:
|
||||
// Should be prevented by the scheduler
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSessionInCache(Session session, ApiResponse response) {
|
||||
session.setStatusCode(response.getResponseCode());
|
||||
session.setQuestions(response.getResults());
|
||||
cacheService.addToCache(session.getId(), session);
|
||||
}
|
||||
|
||||
public void handleCategoryResponse(CategoryResponse categoryResponse) {
|
||||
dataService.saveCategories(categoryResponse.getTrivia_categories());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.ninjaws.quiz.services;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.ninjaws.quiz.models.Request;
|
||||
|
||||
@Service
|
||||
public class SchedulerService {
|
||||
|
||||
@Autowired
|
||||
private QueueService queueService;
|
||||
@Autowired
|
||||
private ApiService apiService;
|
||||
@Autowired
|
||||
private StateManagementService stateService;
|
||||
|
||||
public SchedulerService() {}
|
||||
|
||||
/**
|
||||
* Every 5 seconds:
|
||||
* - Check if there is a request in queue
|
||||
* - Yes: Send it to the apiService
|
||||
* - No: Have the apiService do bulk gathering for the DB
|
||||
*/
|
||||
@Scheduled(fixedDelay = 5000)
|
||||
protected void processQueue() {
|
||||
Request request = queueService.popFromQueue();
|
||||
|
||||
if (request != null) {
|
||||
apiService.handleRequest(request);
|
||||
} else {
|
||||
if (stateService.getCurrentProcessingState() == StateManagementService.ProcessingState.STARTUP || stateService.getCurrentProcessingState() == StateManagementService.ProcessingState.REFRESH) {
|
||||
apiService.collectBulk();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the renewing of the questions, to ensure they stay fresh
|
||||
*/
|
||||
@Scheduled(cron = "0 0 1 * * ?")
|
||||
protected void StartDbRefresh() {
|
||||
apiService.getSessionToken();
|
||||
stateService.setCurrentProcessingState(StateManagementService.ProcessingState.REFRESH);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.ninjaws.quiz.services;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class StateManagementService {
|
||||
|
||||
/**
|
||||
* STARTUP: When starting the app
|
||||
* REFRESH: Scheduled at night, so the database stays fresh
|
||||
* NORMAL: No special activities
|
||||
*/
|
||||
public enum ProcessingState {
|
||||
STARTUP,
|
||||
REFRESH,
|
||||
NORMAL
|
||||
}
|
||||
|
||||
private ProcessingState currentProcessingState;
|
||||
|
||||
public ProcessingState getCurrentProcessingState() {
|
||||
return currentProcessingState;
|
||||
}
|
||||
|
||||
public void setCurrentProcessingState(ProcessingState currentProcessingState) {
|
||||
this.currentProcessingState = currentProcessingState;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user