Skip to content

Commit 2187d6e

Browse files
Merge pull request #39 from Wassim-Rached/staging
Staging v0.1.5
2 parents 7577991 + 5cd613b commit 2187d6e

File tree

57 files changed

+2670
-604
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+2670
-604
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,16 @@ services:
2727
- "172.17.0.1:7777:7777"
2828
volumes:
2929
- ./volumes/servers.json:/pgadmin4/servers.json
30+
31+
quizhub-api:
32+
container_name: quizhub-api
33+
build:
34+
context: .
35+
dockerfile: Dockerfile
36+
restart: always
37+
# environment:
38+
# SPRING_PROFILES_ACTIVE: dev
39+
ports:
40+
- "8080:8080"
41+
depends_on:
42+
- postgres

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@
2323
<artifactId>spring-boot-starter-data-jpa</artifactId>
2424
</dependency>
2525

26+
<!-- spring-boot-starter-validation-->
27+
<dependency>
28+
<groupId>org.springframework.boot</groupId>
29+
<artifactId>spring-boot-starter-validation</artifactId>
30+
</dependency>
31+
2632
<dependency>
2733
<groupId>org.modelmapper</groupId>
2834
<artifactId>modelmapper</artifactId>

src/main/asciidoc/index.adoc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ The QuizHub Backend Server provides RESTful APIs for managing quizzes, users, an
2020
include::{snippets}/quiz/search-quizzes/http-request.adoc[]
2121
include::{snippets}/quiz/search-quizzes/http-response.adoc[]
2222

23+
=== Get Quiz By ID
24+
.Get a quiz by its ID
25+
include::{snippets}/quiz/get-quiz-by-id/http-request.adoc[]
26+
include::{snippets}/quiz/get-quiz-by-id/http-response.adoc[]
27+
2328
=== Create Quiz
2429
.Create a new quiz
2530
include::{snippets}/quiz/create-quiz/http-request.adoc[]

src/main/java/org/wa55death405/quizhub/advices/ControllerExceptionHandler.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,26 @@
11
package org.wa55death405.quizhub.advices;
22

33
import jakarta.persistence.EntityNotFoundException;
4+
import lombok.RequiredArgsConstructor;
5+
import org.springframework.context.annotation.Profile;
46
import org.springframework.http.HttpStatus;
57
import org.springframework.http.ResponseEntity;
8+
import org.springframework.web.HttpRequestMethodNotSupportedException;
69
import org.springframework.web.bind.annotation.ControllerAdvice;
710
import org.springframework.web.bind.annotation.ExceptionHandler;
811
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
12+
import org.springframework.dao.DataIntegrityViolationException;
913
import org.wa55death405.quizhub.dto.StandardApiResponse;
14+
import org.wa55death405.quizhub.entities.ErrorLog;
1015
import org.wa55death405.quizhub.enums.StandardApiStatus;
1116
import org.wa55death405.quizhub.exceptions.InputValidationException;
17+
import org.wa55death405.quizhub.exceptions.IrregularBehaviorException;
1218
import org.wa55death405.quizhub.exceptions.RateLimitReachedException;
19+
import org.wa55death405.quizhub.repositories.ErrorLogRepository;
1320

1421
import java.io.FileNotFoundException;
22+
import java.time.LocalDateTime;
23+
import java.util.Objects;
1524

1625

1726
/*
@@ -52,4 +61,17 @@ public ResponseEntity<StandardApiResponse<Void>> handleFileNotFoundException(Fil
5261
public ResponseEntity<StandardApiResponse<Void>> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) {
5362
return new ResponseEntity<>(new StandardApiResponse<>(StandardApiStatus.FAILURE, "Invalid value for parameter "+ e.getPropertyName() +". Expected type: "+ e.getRequiredType()), HttpStatus.BAD_REQUEST);
5463
}
64+
65+
@ExceptionHandler(DataIntegrityViolationException.class)
66+
public ResponseEntity<StandardApiResponse<Void>> handleDataIntegrityViolationException(DataIntegrityViolationException e) {
67+
String rootCauseMessage = Objects.requireNonNull(e.getRootCause()).getMessage();
68+
69+
return new ResponseEntity<>(new StandardApiResponse<>(StandardApiStatus.FAILURE, rootCauseMessage), HttpStatus.BAD_REQUEST);
70+
}
71+
72+
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
73+
public ResponseEntity<StandardApiResponse<Void>> handleMethodNotSupported(HttpRequestMethodNotSupportedException e) {
74+
return new ResponseEntity<>(new StandardApiResponse<>(StandardApiStatus.FAILURE, e.getMessage()), HttpStatus.METHOD_NOT_ALLOWED);
75+
}
76+
5577
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package org.wa55death405.quizhub.advices;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import org.springframework.context.annotation.Profile;
5+
import org.springframework.http.HttpStatus;
6+
import org.springframework.http.ResponseEntity;
7+
import org.springframework.web.bind.annotation.ControllerAdvice;
8+
import org.springframework.web.bind.annotation.ExceptionHandler;
9+
import org.wa55death405.quizhub.dto.StandardApiResponse;
10+
import org.wa55death405.quizhub.entities.ErrorLog;
11+
import org.wa55death405.quizhub.enums.StandardApiStatus;
12+
import org.wa55death405.quizhub.exceptions.IrregularBehaviorException;
13+
import org.wa55death405.quizhub.repositories.ErrorLogRepository;
14+
15+
import java.time.LocalDateTime;
16+
17+
@ControllerAdvice
18+
@Profile({"prod"})
19+
@RequiredArgsConstructor
20+
public class ProductionExceptionHandler {
21+
22+
private final ErrorLogRepository errorLogRepository;
23+
24+
@ExceptionHandler(IrregularBehaviorException.class)
25+
public ResponseEntity<StandardApiResponse<Void>> handleIrregularBehaviorException(IrregularBehaviorException e) {
26+
ErrorLog errorLog = ErrorLog.builder()
27+
.exceptionMessage(e.getMessage())
28+
.stackTrace(ErrorLog.getStackTraceAsString(e))
29+
.build();
30+
errorLogRepository.save(errorLog);
31+
return new ResponseEntity<>(new StandardApiResponse<>(StandardApiStatus.FAILURE, "Something Irregular just happened"), HttpStatus.INTERNAL_SERVER_ERROR);
32+
}
33+
34+
@ExceptionHandler(Exception.class)
35+
public ResponseEntity<StandardApiResponse<Void>> handleException(Exception e) {
36+
ErrorLog errorLog = ErrorLog.builder()
37+
.exceptionMessage(e.getMessage())
38+
.stackTrace(ErrorLog.getStackTraceAsString(e))
39+
.timestamp(LocalDateTime.now())
40+
.build();
41+
errorLogRepository.save(errorLog);
42+
return new ResponseEntity<>(new StandardApiResponse<>(StandardApiStatus.FAILURE, "Something Went Wrong!"), HttpStatus.INTERNAL_SERVER_ERROR);
43+
}
44+
}

src/main/java/org/wa55death405/quizhub/configurations/SecurityConfiguration.java

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,35 @@
11
package org.wa55death405.quizhub.configurations;
22

3+
import org.springframework.beans.factory.annotation.Value;
4+
import org.springframework.context.annotation.Bean;
35
import org.springframework.context.annotation.Configuration;
6+
import org.springframework.web.servlet.config.annotation.CorsRegistry;
7+
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
8+
9+
import java.util.Arrays;
10+
import java.util.List;
411

512
/*
613
* This configuration class is responsible for
714
* setting up the security of the application
8-
* using Spring Security
15+
* mainly using Spring Security
16+
* it also sets up the CORS policy
917
*/
1018

1119
@Configuration
1220
public class SecurityConfiguration {
13-
21+
@Value("${app.CORS.ALLOWED-ORIGINS:*}")
22+
private String ALLOWED_ORIGINS;
23+
@Value("${app.CORS.ALLOWED-METHODS:*}")
24+
private String ALLOWED_METHODS;
25+
@Value("${app.CORS.ALLOWED-HEADERS:*}")
26+
private String ALLOWED_HEADERS;
27+
@Value("${app.CORS.EXPOSED-HEADERS:*}")
28+
private String EXPOSED_HEADERS;
29+
@Value("${app.CORS.ALLOW-CREDENTIALS:true}")
30+
private String ALLOW_CREDENTIALS;
31+
@Value("${app.CORS.MAX-AGE:3600}")
32+
private String MAX_AGE;
1433

1534
// @Bean
1635
// public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
@@ -21,4 +40,29 @@ public class SecurityConfiguration {
2140
//// .formLogin(Customizer.withDefaults());
2241
// return http.build();
2342
// }
43+
44+
@Bean
45+
public WebMvcConfigurer corsConfigurer()
46+
{
47+
List<String> allowedOrigins = Arrays.asList(ALLOWED_ORIGINS.split(","));
48+
List<String> allowedMethods = Arrays.asList(ALLOWED_METHODS.split(","));
49+
List<String> allowedHeaders = Arrays.asList(ALLOWED_HEADERS.split(","));
50+
List<String> exposedHeaders = Arrays.asList(EXPOSED_HEADERS.split(","));
51+
boolean allowCredentials = Boolean.parseBoolean(ALLOW_CREDENTIALS);
52+
long maxAge = Long.parseLong(MAX_AGE);
53+
54+
return new WebMvcConfigurer() {
55+
@Override
56+
public void addCorsMappings(CorsRegistry registry) {
57+
registry
58+
.addMapping("/**")
59+
.allowedOrigins(allowedOrigins.toArray(new String[0]))
60+
.allowedMethods(allowedMethods.toArray(new String[0]))
61+
.allowedHeaders(allowedHeaders.toArray(new String[0]))
62+
.exposedHeaders(exposedHeaders.toArray(new String[0]))
63+
.allowCredentials(allowCredentials)
64+
.maxAge(maxAge);
65+
}
66+
};
67+
}
2468
}

src/main/java/org/wa55death405/quizhub/controllers/QuizController.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.wa55death405.quizhub.dto.quiz.QuizGeneralInfoDTO;
1212
import org.wa55death405.quizhub.dto.quizAttempt.QuizAttemptResultDTO;
1313
import org.wa55death405.quizhub.dto.quizAttempt.QuizAttemptTakingDTO;
14+
import org.wa55death405.quizhub.enums.QuizAccessType;
1415
import org.wa55death405.quizhub.enums.StandardApiStatus;
1516
import org.wa55death405.quizhub.interfaces.services.IQuizService;
1617

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

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

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

120132
}

src/main/java/org/wa55death405/quizhub/dto/StandardPageList.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
public class StandardPageList<T>{
1313
private Integer currentPage;
1414
private Integer currentItemsSize;
15+
// TODO: change 'totalItems' to 'totalItemsSize'
1516
private Long totalItems;
1617
private List<T> items;
1718
}
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.wa55death405.quizhub.dto.orderedOptionAttempt;
22

33
import lombok.Data;
4+
import org.wa55death405.quizhub.dto.orderedOption.OrderedOptionGeneralDTO;
45
import org.wa55death405.quizhub.entities.OrderedOptionAttempt;
56

67
import java.util.UUID;
@@ -9,12 +10,14 @@
910
public class OrderedOptionAttemptResultDTO {
1011
private UUID id;
1112
private Integer position;
13+
private OrderedOptionGeneralDTO orderedOption;
1214
private Boolean isCorrect;
1315

14-
// TODO : i think something is worng here where is the option id ? and who's this id
16+
// TODO: the 'id' field is might be unnecessary
1517
public OrderedOptionAttemptResultDTO(OrderedOptionAttempt orderedOptionAttempt) {
1618
this.id = orderedOptionAttempt.getId();
1719
this.position = orderedOptionAttempt.getPosition();
1820
this.isCorrect = orderedOptionAttempt.getIsCorrect();
21+
this.orderedOption = new OrderedOptionGeneralDTO(orderedOptionAttempt.getOrderedOption());
1922
}
2023
}

0 commit comments

Comments
 (0)