Skip to content

Commit

Permalink
Merge pull request #39 from Wassim-Rached/staging
Browse files Browse the repository at this point in the history
Staging v0.1.5
  • Loading branch information
Wassim-Rached authored Jun 24, 2024
2 parents 7577991 + 5cd613b commit 2187d6e
Show file tree
Hide file tree
Showing 57 changed files with 2,670 additions and 604 deletions.
13 changes: 13 additions & 0 deletions docker-compose.yml → docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,16 @@ services:
- "172.17.0.1:7777:7777"
volumes:
- ./volumes/servers.json:/pgadmin4/servers.json

quizhub-api:
container_name: quizhub-api
build:
context: .
dockerfile: Dockerfile
restart: always
# environment:
# SPRING_PROFILES_ACTIVE: dev
ports:
- "8080:8080"
depends_on:
- postgres
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<!-- spring-boot-starter-validation-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
Expand Down
5 changes: 5 additions & 0 deletions src/main/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ The QuizHub Backend Server provides RESTful APIs for managing quizzes, users, an
include::{snippets}/quiz/search-quizzes/http-request.adoc[]
include::{snippets}/quiz/search-quizzes/http-response.adoc[]

=== Get Quiz By ID
.Get a quiz by its ID
include::{snippets}/quiz/get-quiz-by-id/http-request.adoc[]
include::{snippets}/quiz/get-quiz-by-id/http-response.adoc[]

=== Create Quiz
.Create a new quiz
include::{snippets}/quiz/create-quiz/http-request.adoc[]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
package org.wa55death405.quizhub.advices;

import jakarta.persistence.EntityNotFoundException;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Profile;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.dao.DataIntegrityViolationException;
import org.wa55death405.quizhub.dto.StandardApiResponse;
import org.wa55death405.quizhub.entities.ErrorLog;
import org.wa55death405.quizhub.enums.StandardApiStatus;
import org.wa55death405.quizhub.exceptions.InputValidationException;
import org.wa55death405.quizhub.exceptions.IrregularBehaviorException;
import org.wa55death405.quizhub.exceptions.RateLimitReachedException;
import org.wa55death405.quizhub.repositories.ErrorLogRepository;

import java.io.FileNotFoundException;
import java.time.LocalDateTime;
import java.util.Objects;


/*
Expand Down Expand Up @@ -52,4 +61,17 @@ public ResponseEntity<StandardApiResponse<Void>> handleFileNotFoundException(Fil
public ResponseEntity<StandardApiResponse<Void>> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) {
return new ResponseEntity<>(new StandardApiResponse<>(StandardApiStatus.FAILURE, "Invalid value for parameter "+ e.getPropertyName() +". Expected type: "+ e.getRequiredType()), HttpStatus.BAD_REQUEST);
}

@ExceptionHandler(DataIntegrityViolationException.class)
public ResponseEntity<StandardApiResponse<Void>> handleDataIntegrityViolationException(DataIntegrityViolationException e) {
String rootCauseMessage = Objects.requireNonNull(e.getRootCause()).getMessage();

return new ResponseEntity<>(new StandardApiResponse<>(StandardApiStatus.FAILURE, rootCauseMessage), HttpStatus.BAD_REQUEST);
}

@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public ResponseEntity<StandardApiResponse<Void>> handleMethodNotSupported(HttpRequestMethodNotSupportedException e) {
return new ResponseEntity<>(new StandardApiResponse<>(StandardApiStatus.FAILURE, e.getMessage()), HttpStatus.METHOD_NOT_ALLOWED);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.wa55death405.quizhub.advices;

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Profile;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.wa55death405.quizhub.dto.StandardApiResponse;
import org.wa55death405.quizhub.entities.ErrorLog;
import org.wa55death405.quizhub.enums.StandardApiStatus;
import org.wa55death405.quizhub.exceptions.IrregularBehaviorException;
import org.wa55death405.quizhub.repositories.ErrorLogRepository;

import java.time.LocalDateTime;

@ControllerAdvice
@Profile({"prod"})
@RequiredArgsConstructor
public class ProductionExceptionHandler {

private final ErrorLogRepository errorLogRepository;

@ExceptionHandler(IrregularBehaviorException.class)
public ResponseEntity<StandardApiResponse<Void>> handleIrregularBehaviorException(IrregularBehaviorException e) {
ErrorLog errorLog = ErrorLog.builder()
.exceptionMessage(e.getMessage())
.stackTrace(ErrorLog.getStackTraceAsString(e))
.build();
errorLogRepository.save(errorLog);
return new ResponseEntity<>(new StandardApiResponse<>(StandardApiStatus.FAILURE, "Something Irregular just happened"), HttpStatus.INTERNAL_SERVER_ERROR);
}

@ExceptionHandler(Exception.class)
public ResponseEntity<StandardApiResponse<Void>> handleException(Exception e) {
ErrorLog errorLog = ErrorLog.builder()
.exceptionMessage(e.getMessage())
.stackTrace(ErrorLog.getStackTraceAsString(e))
.timestamp(LocalDateTime.now())
.build();
errorLogRepository.save(errorLog);
return new ResponseEntity<>(new StandardApiResponse<>(StandardApiStatus.FAILURE, "Something Went Wrong!"), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,35 @@
package org.wa55death405.quizhub.configurations;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.Arrays;
import java.util.List;

/*
* This configuration class is responsible for
* setting up the security of the application
* using Spring Security
* mainly using Spring Security
* it also sets up the CORS policy
*/

@Configuration
public class SecurityConfiguration {

@Value("${app.CORS.ALLOWED-ORIGINS:*}")
private String ALLOWED_ORIGINS;
@Value("${app.CORS.ALLOWED-METHODS:*}")
private String ALLOWED_METHODS;
@Value("${app.CORS.ALLOWED-HEADERS:*}")
private String ALLOWED_HEADERS;
@Value("${app.CORS.EXPOSED-HEADERS:*}")
private String EXPOSED_HEADERS;
@Value("${app.CORS.ALLOW-CREDENTIALS:true}")
private String ALLOW_CREDENTIALS;
@Value("${app.CORS.MAX-AGE:3600}")
private String MAX_AGE;

// @Bean
// public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
Expand All @@ -21,4 +40,29 @@ public class SecurityConfiguration {
//// .formLogin(Customizer.withDefaults());
// return http.build();
// }

@Bean
public WebMvcConfigurer corsConfigurer()
{
List<String> allowedOrigins = Arrays.asList(ALLOWED_ORIGINS.split(","));
List<String> allowedMethods = Arrays.asList(ALLOWED_METHODS.split(","));
List<String> allowedHeaders = Arrays.asList(ALLOWED_HEADERS.split(","));
List<String> exposedHeaders = Arrays.asList(EXPOSED_HEADERS.split(","));
boolean allowCredentials = Boolean.parseBoolean(ALLOW_CREDENTIALS);
long maxAge = Long.parseLong(MAX_AGE);

return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry
.addMapping("/**")
.allowedOrigins(allowedOrigins.toArray(new String[0]))
.allowedMethods(allowedMethods.toArray(new String[0]))
.allowedHeaders(allowedHeaders.toArray(new String[0]))
.exposedHeaders(exposedHeaders.toArray(new String[0]))
.allowCredentials(allowCredentials)
.maxAge(maxAge);
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.wa55death405.quizhub.dto.quiz.QuizGeneralInfoDTO;
import org.wa55death405.quizhub.dto.quizAttempt.QuizAttemptResultDTO;
import org.wa55death405.quizhub.dto.quizAttempt.QuizAttemptTakingDTO;
import org.wa55death405.quizhub.enums.QuizAccessType;
import org.wa55death405.quizhub.enums.StandardApiStatus;
import org.wa55death405.quizhub.interfaces.services.IQuizService;

Expand Down Expand Up @@ -83,6 +84,7 @@ public ResponseEntity<StandardApiResponse<Float>> finishQuizAttempt(@PathVariabl
*/
@PostMapping
public ResponseEntity<StandardApiResponse<UUID>> createQuiz(@RequestBody QuizCreationDTO body) {
body.setQuizAccessType(QuizAccessType.LINKED);
return new ResponseEntity<>(new StandardApiResponse<>(StandardApiStatus.SUCCESS,"Quiz created successfully",quizService.createQuiz(body).getId()), HttpStatus.CREATED);
}

Expand Down Expand Up @@ -116,5 +118,15 @@ public ResponseEntity<StandardApiResponse<QuizAttemptResultDTO>> getQuizAttemptR
return new ResponseEntity<>(new StandardApiResponse<>(StandardApiStatus.SUCCESS,"Quiz attempt result fetched successfully",quizService.getQuizAttemptResult(quizAttemptId)), HttpStatus.OK);
}

/*
this api is used to get the quiz by id
it takes the quiz id as a path variable
and returns the quiz
*/
@GetMapping("/{quizId}")
public ResponseEntity<StandardApiResponse<QuizGeneralInfoDTO>> getQuiz(@PathVariable UUID quizId) {
return new ResponseEntity<>(new StandardApiResponse<>(StandardApiStatus.SUCCESS,"Quiz fetched successfully",quizService.getQuizById(quizId)), HttpStatus.OK);
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
public class StandardPageList<T>{
private Integer currentPage;
private Integer currentItemsSize;
// TODO: change 'totalItems' to 'totalItemsSize'
private Long totalItems;
private List<T> items;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.wa55death405.quizhub.dto.orderedOptionAttempt;

import lombok.Data;
import org.wa55death405.quizhub.dto.orderedOption.OrderedOptionGeneralDTO;
import org.wa55death405.quizhub.entities.OrderedOptionAttempt;

import java.util.UUID;
Expand All @@ -9,12 +10,14 @@
public class OrderedOptionAttemptResultDTO {
private UUID id;
private Integer position;
private OrderedOptionGeneralDTO orderedOption;
private Boolean isCorrect;

// TODO : i think something is worng here where is the option id ? and who's this id
// TODO: the 'id' field is might be unnecessary
public OrderedOptionAttemptResultDTO(OrderedOptionAttempt orderedOptionAttempt) {
this.id = orderedOptionAttempt.getId();
this.position = orderedOptionAttempt.getPosition();
this.isCorrect = orderedOptionAttempt.getIsCorrect();
this.orderedOption = new OrderedOptionGeneralDTO(orderedOptionAttempt.getOrderedOption());
}
}
Loading

0 comments on commit 2187d6e

Please sign in to comment.