From 81b42a00851f22ec6dc02e3859aa9525e8538c68 Mon Sep 17 00:00:00 2001 From: sergioqfeg1 Date: Tue, 16 Apr 2024 17:21:19 +0200 Subject: [PATCH 01/30] feat: getUsers controller --- .../en2b/quizapi/commons/user/UserController.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/api/src/main/java/lab/en2b/quizapi/commons/user/UserController.java b/api/src/main/java/lab/en2b/quizapi/commons/user/UserController.java index 5885412a..72b5978d 100644 --- a/api/src/main/java/lab/en2b/quizapi/commons/user/UserController.java +++ b/api/src/main/java/lab/en2b/quizapi/commons/user/UserController.java @@ -4,10 +4,9 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import java.util.List; @RestController @RequestMapping("/users") @@ -28,4 +27,9 @@ public ResponseEntity getUserDetails(Authentication authenticat return ResponseEntity.ok(userService.getUserDetailsByAuthentication(authentication)); } + @GetMapping + public ResponseEntity> getUsers() { + return ResponseEntity.ok(userService.getUsers()); + } + } From 932a4ebe72f3f109279d9c0553ca06ab5061b8ae Mon Sep 17 00:00:00 2001 From: sergioqfeg1 Date: Tue, 16 Apr 2024 17:21:29 +0200 Subject: [PATCH 02/30] feat: getUsers service --- .../java/lab/en2b/quizapi/commons/user/UserService.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/api/src/main/java/lab/en2b/quizapi/commons/user/UserService.java b/api/src/main/java/lab/en2b/quizapi/commons/user/UserService.java index 2c4f44cc..1397736e 100644 --- a/api/src/main/java/lab/en2b/quizapi/commons/user/UserService.java +++ b/api/src/main/java/lab/en2b/quizapi/commons/user/UserService.java @@ -15,8 +15,10 @@ import org.springframework.stereotype.Service; import java.time.Instant; +import java.util.List; import java.util.Optional; import java.util.UUID; +import java.util.stream.Collectors; @Service @RequiredArgsConstructor @@ -71,4 +73,8 @@ public User getUserByAuthentication(Authentication authentication) { public UserResponseDto getUserDetailsByAuthentication(Authentication authentication) { return userResponseDtoMapper.apply(getUserByAuthentication(authentication)); } + + public List getUsers() { + return userRepository.findAll().stream().map(userResponseDtoMapper).collect(Collectors.toList()); + } } From 43b17778284afb755c8710ee25750cffc129057e Mon Sep 17 00:00:00 2001 From: sergioqfeg1 Date: Tue, 16 Apr 2024 17:22:37 +0200 Subject: [PATCH 03/30] feat: getUser controller --- .../java/lab/en2b/quizapi/commons/user/UserController.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/api/src/main/java/lab/en2b/quizapi/commons/user/UserController.java b/api/src/main/java/lab/en2b/quizapi/commons/user/UserController.java index 72b5978d..45c6eae2 100644 --- a/api/src/main/java/lab/en2b/quizapi/commons/user/UserController.java +++ b/api/src/main/java/lab/en2b/quizapi/commons/user/UserController.java @@ -32,4 +32,9 @@ public ResponseEntity> getUsers() { return ResponseEntity.ok(userService.getUsers()); } + @GetMapping("/{id}") + public ResponseEntity getUser(@PathVariable Long id) { + return ResponseEntity.ok(userService.getUser(id)); + } + } From 6074008578ae4619b3558c4a487f06bae76bf530 Mon Sep 17 00:00:00 2001 From: sergioqfeg1 Date: Tue, 16 Apr 2024 17:22:55 +0200 Subject: [PATCH 04/30] feat: getUser service --- .../main/java/lab/en2b/quizapi/commons/user/UserService.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/api/src/main/java/lab/en2b/quizapi/commons/user/UserService.java b/api/src/main/java/lab/en2b/quizapi/commons/user/UserService.java index 1397736e..949741da 100644 --- a/api/src/main/java/lab/en2b/quizapi/commons/user/UserService.java +++ b/api/src/main/java/lab/en2b/quizapi/commons/user/UserService.java @@ -77,4 +77,8 @@ public UserResponseDto getUserDetailsByAuthentication(Authentication authenticat public List getUsers() { return userRepository.findAll().stream().map(userResponseDtoMapper).collect(Collectors.toList()); } + + public UserResponseDto getUser(Long id) { + return userResponseDtoMapper.apply(userRepository.findById(id).orElseThrow()); + } } From e58f66e061e5cf1fae0701be66cca276d8c3c0cf Mon Sep 17 00:00:00 2001 From: sergioqfeg1 Date: Tue, 16 Apr 2024 17:35:40 +0200 Subject: [PATCH 05/30] chore: swagger documentation --- .../quizapi/commons/user/UserController.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/api/src/main/java/lab/en2b/quizapi/commons/user/UserController.java b/api/src/main/java/lab/en2b/quizapi/commons/user/UserController.java index 45c6eae2..5cd2b482 100644 --- a/api/src/main/java/lab/en2b/quizapi/commons/user/UserController.java +++ b/api/src/main/java/lab/en2b/quizapi/commons/user/UserController.java @@ -1,5 +1,10 @@ package lab.en2b.quizapi.commons.user; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import lab.en2b.quizapi.commons.user.dtos.UserResponseDto; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; @@ -22,16 +27,35 @@ public class UserController { * @param authentication the authentication object * @return the response dto for the user details */ + @Operation(summary = "Gets the user details", description = "Gets the user details for the given authentication") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Successfully retrieved"), + @ApiResponse(responseCode = "403", description = "You are not logged in", content = @io.swagger.v3.oas.annotations.media.Content), + }) + @Parameters({ + @Parameter(name = "id", description = "The id of the user to get the details for", example = "1") + }) @GetMapping("/details") public ResponseEntity getUserDetails(Authentication authentication) { return ResponseEntity.ok(userService.getUserDetailsByAuthentication(authentication)); } + @Operation(summary = "Gets all users", description = "Gets all users") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Successfully retrieved"), + }) @GetMapping public ResponseEntity> getUsers() { return ResponseEntity.ok(userService.getUsers()); } + @Operation(summary = "Gets a user", description = "Gets a user") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Successfully retrieved"), + }) + @Parameters({ + @Parameter(name = "id", description = "The id of the user to get", example = "1") + }) @GetMapping("/{id}") public ResponseEntity getUser(@PathVariable Long id) { return ResponseEntity.ok(userService.getUser(id)); From d5ea74c7b35b4a2551e6013661439b8646e93f2f Mon Sep 17 00:00:00 2001 From: sergioqfeg1 Date: Tue, 16 Apr 2024 17:35:40 +0200 Subject: [PATCH 06/30] chore: swagger documentation --- .../quizapi/commons/user/UserController.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/api/src/main/java/lab/en2b/quizapi/commons/user/UserController.java b/api/src/main/java/lab/en2b/quizapi/commons/user/UserController.java index 45c6eae2..35c22453 100644 --- a/api/src/main/java/lab/en2b/quizapi/commons/user/UserController.java +++ b/api/src/main/java/lab/en2b/quizapi/commons/user/UserController.java @@ -1,5 +1,10 @@ package lab.en2b.quizapi.commons.user; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import lab.en2b.quizapi.commons.user.dtos.UserResponseDto; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; @@ -22,16 +27,32 @@ public class UserController { * @param authentication the authentication object * @return the response dto for the user details */ + @Operation(summary = "Gets the user details", description = "Gets the user details for the given authentication") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Successfully retrieved"), + @ApiResponse(responseCode = "403", description = "You are not logged in", content = @io.swagger.v3.oas.annotations.media.Content), + }) @GetMapping("/details") public ResponseEntity getUserDetails(Authentication authentication) { return ResponseEntity.ok(userService.getUserDetailsByAuthentication(authentication)); } + @Operation(summary = "Gets all users", description = "Gets all users") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Successfully retrieved"), + }) @GetMapping public ResponseEntity> getUsers() { return ResponseEntity.ok(userService.getUsers()); } + @Operation(summary = "Gets a user", description = "Gets a user") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Successfully retrieved"), + }) + @Parameters({ + @Parameter(name = "id", description = "The id of the user to get", example = "1") + }) @GetMapping("/{id}") public ResponseEntity getUser(@PathVariable Long id) { return ResponseEntity.ok(userService.getUser(id)); From 7b1b285ba4cd406fe745c960b44ececc68efc362 Mon Sep 17 00:00:00 2001 From: sergioqfeg1 Date: Wed, 17 Apr 2024 17:04:17 +0200 Subject: [PATCH 07/30] fix: names of tests --- .../test/java/lab/en2b/quizapi/user/UserControllerTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/test/java/lab/en2b/quizapi/user/UserControllerTest.java b/api/src/test/java/lab/en2b/quizapi/user/UserControllerTest.java index 4d090f5e..15a98ce3 100644 --- a/api/src/test/java/lab/en2b/quizapi/user/UserControllerTest.java +++ b/api/src/test/java/lab/en2b/quizapi/user/UserControllerTest.java @@ -31,14 +31,14 @@ public class UserControllerTest { UserService userService; @Test - void getUserShouldReturn200() throws Exception{ + void getPersonalDetailsShouldReturn200() throws Exception{ mockMvc.perform(get("/users/details") .with(user("test").roles("user"))) .andExpect(status().isOk()); } @Test - void getUserShouldReturn403() throws Exception{ + void getPersonalDetailsShouldReturn403() throws Exception{ mockMvc.perform(get("/users/details")) .andExpect(status().isForbidden()); } From 762b4ed2451d4efdee2612d244c09931737d1e8b Mon Sep 17 00:00:00 2001 From: sergioqfeg1 Date: Wed, 17 Apr 2024 17:09:20 +0200 Subject: [PATCH 08/30] fix: swagger documentation --- .../main/java/lab/en2b/quizapi/commons/user/UserController.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/src/main/java/lab/en2b/quizapi/commons/user/UserController.java b/api/src/main/java/lab/en2b/quizapi/commons/user/UserController.java index 35c22453..6e91a9f2 100644 --- a/api/src/main/java/lab/en2b/quizapi/commons/user/UserController.java +++ b/api/src/main/java/lab/en2b/quizapi/commons/user/UserController.java @@ -40,6 +40,7 @@ public ResponseEntity getUserDetails(Authentication authenticat @Operation(summary = "Gets all users", description = "Gets all users") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Successfully retrieved"), + @ApiResponse(responseCode = "403", description = "You are not logged in", content = @io.swagger.v3.oas.annotations.media.Content), }) @GetMapping public ResponseEntity> getUsers() { @@ -49,6 +50,7 @@ public ResponseEntity> getUsers() { @Operation(summary = "Gets a user", description = "Gets a user") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Successfully retrieved"), + @ApiResponse(responseCode = "403", description = "You are not logged in", content = @io.swagger.v3.oas.annotations.media.Content), }) @Parameters({ @Parameter(name = "id", description = "The id of the user to get", example = "1") From 0ddf27cf2ec433217a4b043dabc4e443713df175 Mon Sep 17 00:00:00 2001 From: sergioqfeg1 Date: Wed, 17 Apr 2024 17:10:23 +0200 Subject: [PATCH 09/30] test: user controller --- .../en2b/quizapi/user/UserControllerTest.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/api/src/test/java/lab/en2b/quizapi/user/UserControllerTest.java b/api/src/test/java/lab/en2b/quizapi/user/UserControllerTest.java index 15a98ce3..c172dcc0 100644 --- a/api/src/test/java/lab/en2b/quizapi/user/UserControllerTest.java +++ b/api/src/test/java/lab/en2b/quizapi/user/UserControllerTest.java @@ -43,4 +43,30 @@ void getPersonalDetailsShouldReturn403() throws Exception{ .andExpect(status().isForbidden()); } + @Test + void getUsersShouldReturn200() throws Exception{ + mockMvc.perform(get("/users") + .with(user("test").roles("user"))) + .andExpect(status().isOk()); + } + + @Test + void getUsersShouldReturn403() throws Exception{ + mockMvc.perform(get("/users")) + .andExpect(status().isForbidden()); + } + + @Test + void getUserShouldReturn200() throws Exception{ + mockMvc.perform(get("/users/1") + .with(user("test").roles("user"))) + .andExpect(status().isOk()); + } + + @Test + void getUserShouldReturn403() throws Exception{ + mockMvc.perform(get("/users/1")) + .andExpect(status().isForbidden()); + } + } From d8c609970990ff8ef5c3e5a07d61337e8e1cee55 Mon Sep 17 00:00:00 2001 From: sergioqfeg1 Date: Wed, 17 Apr 2024 17:12:21 +0200 Subject: [PATCH 10/30] fix: test names --- api/src/test/java/lab/en2b/quizapi/user/UserServiceTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/test/java/lab/en2b/quizapi/user/UserServiceTest.java b/api/src/test/java/lab/en2b/quizapi/user/UserServiceTest.java index b99981af..b1ede558 100644 --- a/api/src/test/java/lab/en2b/quizapi/user/UserServiceTest.java +++ b/api/src/test/java/lab/en2b/quizapi/user/UserServiceTest.java @@ -55,7 +55,7 @@ public void setUp() { } @Test - public void getUserDetailsTest(){ + public void getPersonalDetailsTest(){ Authentication authentication = mock(Authentication.class); when(authentication.getPrincipal()).thenReturn(UserDetailsImpl.build(defaultUser)); when(userRepository.findByEmail(any())).thenReturn(Optional.of(defaultUser)); @@ -64,7 +64,7 @@ public void getUserDetailsTest(){ } @Test - public void getUserDetailsWhenNotFound() { + public void getPersonalDetailsWhenNotFound() { Authentication authentication = mock(Authentication.class); when(authentication.getPrincipal()).thenReturn(UserDetailsImpl.build(defaultUser)); when(userRepository.findByEmail(any())).thenReturn(Optional.empty()); From b0fe1e9304a912e3909fe16c01b58763773a038c Mon Sep 17 00:00:00 2001 From: sergioqfeg1 Date: Wed, 17 Apr 2024 17:21:31 +0200 Subject: [PATCH 11/30] test: getUsers service tests --- .../en2b/quizapi/user/UserServiceTest.java | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/api/src/test/java/lab/en2b/quizapi/user/UserServiceTest.java b/api/src/test/java/lab/en2b/quizapi/user/UserServiceTest.java index b1ede558..4b10e8af 100644 --- a/api/src/test/java/lab/en2b/quizapi/user/UserServiceTest.java +++ b/api/src/test/java/lab/en2b/quizapi/user/UserServiceTest.java @@ -17,6 +17,8 @@ import org.springframework.security.core.Authentication; import org.springframework.test.context.junit.jupiter.SpringExtension; +import java.util.ArrayList; +import java.util.List; import java.util.NoSuchElementException; import java.util.Optional; @@ -33,13 +35,16 @@ public class UserServiceTest { @Mock private UserRepository userRepository; + private UserResponseDtoMapper userResponseDtoMapper; + private User defaultUser; private UserResponseDto defaultUserResponseDto; @BeforeEach public void setUp() { - userService = new UserService(userRepository, new UserResponseDtoMapper()); + this.userResponseDtoMapper = new UserResponseDtoMapper(); + userService = new UserService(userRepository, userResponseDtoMapper); defaultUser = User.builder() .id(1L) .username("HordyJurtado") @@ -71,4 +76,39 @@ public void getPersonalDetailsWhenNotFound() { Assertions.assertThrows(NoSuchElementException.class, () -> userService.getUserDetailsByAuthentication(authentication)); } + @Test + public void getUsersTestWhenThereAreMultiplePeople(){ + User defaultUser1 = User.builder() + .id(1L) + .username("HordyJurtado") + .email("test1@test1.com") + .password("password") + .role("ROLE_USER") + .build(); + User defaultUser2 = User.builder() + .id(2L) + .username("HordyJurtado") + .email("test2@test2.com") + .password("password") + .role("ROLE_USER") + .build(); + User defaultUser3 = User.builder() + .id(3L) + .username("HordyJurtado") + .email("test3@test3.com") + .password("password") + .role("ROLE_USER") + .build(); + List userResult = List.of(defaultUser1, defaultUser2, defaultUser3); + when(userRepository.findAll()).thenReturn(userResult); + List result = userResult.stream().map(userResponseDtoMapper::apply).toList(); + Assertions.assertEquals(result, userService.getUsers()); + } + + @Test + public void getUsersTestWhenThereIsNoPeople(){ + when(userRepository.findAll()).thenReturn(new ArrayList<>()); + Assertions.assertEquals(List.of(), userService.getUsers()); + } + } From 874265f31ec570af5f4e9b4f7ca8b2d62f39b8e0 Mon Sep 17 00:00:00 2001 From: sergioqfeg1 Date: Wed, 17 Apr 2024 17:22:38 +0200 Subject: [PATCH 12/30] test: getUser service tests --- .../java/lab/en2b/quizapi/user/UserServiceTest.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/api/src/test/java/lab/en2b/quizapi/user/UserServiceTest.java b/api/src/test/java/lab/en2b/quizapi/user/UserServiceTest.java index 4b10e8af..ed698280 100644 --- a/api/src/test/java/lab/en2b/quizapi/user/UserServiceTest.java +++ b/api/src/test/java/lab/en2b/quizapi/user/UserServiceTest.java @@ -111,4 +111,16 @@ public void getUsersTestWhenThereIsNoPeople(){ Assertions.assertEquals(List.of(), userService.getUsers()); } + @Test + public void getUserTest(){ + when(userRepository.findById(1L)).thenReturn(Optional.of(defaultUser)); + Assertions.assertEquals(defaultUserResponseDto, userService.getUser(1L)); + } + + @Test + public void getUserTestWhenNotFound(){ + when(userRepository.findById(1L)).thenReturn(Optional.empty()); + Assertions.assertThrows(NoSuchElementException.class, () -> userService.getUser(1L)); + } + } From 1cb4cdd5fd13576400259d5e50c7a968a969bbe8 Mon Sep 17 00:00:00 2001 From: Dario Date: Wed, 17 Apr 2024 18:13:21 +0200 Subject: [PATCH 13/30] fix: user endpoints now are open --- .../java/lab/en2b/quizapi/auth/config/SecurityConfig.java | 2 ++ .../lab/en2b/quizapi/commons/user/UserController.java | 6 ++---- .../java/lab/en2b/quizapi/user/UserControllerTest.java | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/src/main/java/lab/en2b/quizapi/auth/config/SecurityConfig.java b/api/src/main/java/lab/en2b/quizapi/auth/config/SecurityConfig.java index 6678b886..5bc59205 100644 --- a/api/src/main/java/lab/en2b/quizapi/auth/config/SecurityConfig.java +++ b/api/src/main/java/lab/en2b/quizapi/auth/config/SecurityConfig.java @@ -58,6 +58,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http, Authentication .cors(Customizer.withDefaults()) .sessionManagement(configuration -> configuration.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(authorize -> authorize + .requestMatchers(HttpMethod.GET,"/users/details").authenticated() + .requestMatchers(HttpMethod.GET,"/users","/users/**").permitAll() .requestMatchers(HttpMethod.GET,"/auth/logout").authenticated() .requestMatchers(HttpMethod.POST,"/auth/**").permitAll() .requestMatchers(HttpMethod.GET, "/swagger/**").permitAll() diff --git a/api/src/main/java/lab/en2b/quizapi/commons/user/UserController.java b/api/src/main/java/lab/en2b/quizapi/commons/user/UserController.java index 6e91a9f2..895c38fe 100644 --- a/api/src/main/java/lab/en2b/quizapi/commons/user/UserController.java +++ b/api/src/main/java/lab/en2b/quizapi/commons/user/UserController.java @@ -39,8 +39,7 @@ public ResponseEntity getUserDetails(Authentication authenticat @Operation(summary = "Gets all users", description = "Gets all users") @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Successfully retrieved"), - @ApiResponse(responseCode = "403", description = "You are not logged in", content = @io.swagger.v3.oas.annotations.media.Content), + @ApiResponse(responseCode = "200", description = "Successfully retrieved") }) @GetMapping public ResponseEntity> getUsers() { @@ -49,8 +48,7 @@ public ResponseEntity> getUsers() { @Operation(summary = "Gets a user", description = "Gets a user") @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Successfully retrieved"), - @ApiResponse(responseCode = "403", description = "You are not logged in", content = @io.swagger.v3.oas.annotations.media.Content), + @ApiResponse(responseCode = "200", description = "Successfully retrieved") }) @Parameters({ @Parameter(name = "id", description = "The id of the user to get", example = "1") diff --git a/api/src/test/java/lab/en2b/quizapi/user/UserControllerTest.java b/api/src/test/java/lab/en2b/quizapi/user/UserControllerTest.java index c172dcc0..bf6df6df 100644 --- a/api/src/test/java/lab/en2b/quizapi/user/UserControllerTest.java +++ b/api/src/test/java/lab/en2b/quizapi/user/UserControllerTest.java @@ -51,9 +51,9 @@ void getUsersShouldReturn200() throws Exception{ } @Test - void getUsersShouldReturn403() throws Exception{ + void getUsersNoAuthShouldReturn200() throws Exception{ mockMvc.perform(get("/users")) - .andExpect(status().isForbidden()); + .andExpect(status().isOk()); } @Test @@ -64,9 +64,9 @@ void getUserShouldReturn200() throws Exception{ } @Test - void getUserShouldReturn403() throws Exception{ + void getUserNoAuthShouldReturn200() throws Exception{ mockMvc.perform(get("/users/1")) - .andExpect(status().isForbidden()); + .andExpect(status().isOk()); } } From 1ee8ad91bed2cde3b20bc28e472a99d46bf2d7e2 Mon Sep 17 00:00:00 2001 From: Dario Date: Wed, 17 Apr 2024 18:29:10 +0200 Subject: [PATCH 14/30] fix: question endpoints now are open --- .../quizapi/auth/config/SecurityConfig.java | 2 ++ .../question/QuestionController.java | 5 +--- .../questions/QuestionControllerTest.java | 25 ++++++++++--------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/api/src/main/java/lab/en2b/quizapi/auth/config/SecurityConfig.java b/api/src/main/java/lab/en2b/quizapi/auth/config/SecurityConfig.java index 5bc59205..2d6848a0 100644 --- a/api/src/main/java/lab/en2b/quizapi/auth/config/SecurityConfig.java +++ b/api/src/main/java/lab/en2b/quizapi/auth/config/SecurityConfig.java @@ -58,6 +58,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http, Authentication .cors(Customizer.withDefaults()) .sessionManagement(configuration -> configuration.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(authorize -> authorize + .requestMatchers(HttpMethod.POST,"/questions/**/answer").permitAll() + .requestMatchers(HttpMethod.GET,"/questions/**").permitAll() .requestMatchers(HttpMethod.GET,"/users/details").authenticated() .requestMatchers(HttpMethod.GET,"/users","/users/**").permitAll() .requestMatchers(HttpMethod.GET,"/auth/logout").authenticated() diff --git a/api/src/main/java/lab/en2b/quizapi/questions/question/QuestionController.java b/api/src/main/java/lab/en2b/quizapi/questions/question/QuestionController.java index e81ca5ac..c25d22b0 100644 --- a/api/src/main/java/lab/en2b/quizapi/questions/question/QuestionController.java +++ b/api/src/main/java/lab/en2b/quizapi/questions/question/QuestionController.java @@ -21,7 +21,6 @@ public class QuestionController { @Operation(summary = "Sends an answer", description = "Sends the answer dto for a given question id") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Successfully retrieved"), - @ApiResponse(responseCode = "403", description = "You are not logged in", content = @io.swagger.v3.oas.annotations.media.Content), @ApiResponse(responseCode = "404", description = "Not found - There is not a question with that id", content = @io.swagger.v3.oas.annotations.media.Content) }) @PostMapping("/{questionId}/answer") @@ -32,10 +31,9 @@ private ResponseEntity answerQuestion(@PathVariable @Pos @Operation(summary = "Gets a random question", description = "Gets a random question in the language") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Successfully retrieved"), - @ApiResponse(responseCode = "403", description = "You are not logged in", content = @io.swagger.v3.oas.annotations.media.Content), @ApiResponse(responseCode = "404", description = "Language does not exist or it is misspelled", content = @io.swagger.v3.oas.annotations.media.Content) }) - @GetMapping("/new") + @GetMapping("/random") private ResponseEntity generateQuestion(@RequestParam(required = false) String lang){ return ResponseEntity.ok(questionService.getRandomQuestion(lang)); } @@ -43,7 +41,6 @@ private ResponseEntity generateQuestion(@RequestParam(requi @Operation(summary = "Gets a question", description = "Gets a question given a question id") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Successfully retrieved"), - @ApiResponse(responseCode = "403", description = "You are not logged in", content = @io.swagger.v3.oas.annotations.media.Content), @ApiResponse(responseCode = "404", description = "Not found - There is not a question with that id", content = @io.swagger.v3.oas.annotations.media.Content) }) @GetMapping("/{id}") diff --git a/api/src/test/java/lab/en2b/quizapi/questions/QuestionControllerTest.java b/api/src/test/java/lab/en2b/quizapi/questions/QuestionControllerTest.java index 4bf7be42..e175a517 100644 --- a/api/src/test/java/lab/en2b/quizapi/questions/QuestionControllerTest.java +++ b/api/src/test/java/lab/en2b/quizapi/questions/QuestionControllerTest.java @@ -35,16 +35,16 @@ public class QuestionControllerTest { UserService userService; @Test - void newQuestionShouldReturn403() throws Exception{ - mockMvc.perform(get("/questions/new?lang=en") + void randomQuestionNoAuthShouldReturn200() throws Exception{ + mockMvc.perform(get("/questions/random?lang=en") .contentType("application/json") .with(csrf())) - .andExpect(status().isForbidden()); + .andExpect(status().isOk()); } @Test - void newQuestionShouldReturn200() throws Exception{ - mockMvc.perform(get("/questions/new?lang=en") + void randomQuestionShouldReturn200() throws Exception{ + mockMvc.perform(get("/questions/random?lang=en") .with(user("test").roles("user")) .contentType("application/json") .with(csrf())) @@ -52,8 +52,8 @@ void newQuestionShouldReturn200() throws Exception{ } @Test - void newQuestionNoLangShouldReturn200() throws Exception{ - mockMvc.perform(get("/questions/new") + void randomQuestionNoLangShouldReturn200() throws Exception{ + mockMvc.perform(get("/questions/random") .with(user("test").roles("user")) .contentType("application/json") .with(csrf())) @@ -61,11 +61,11 @@ void newQuestionNoLangShouldReturn200() throws Exception{ } @Test - void questionByIdShouldReturn403() throws Exception{ + void questionByIdNoAuthShouldReturn200() throws Exception{ mockMvc.perform(get("/questions/1") .contentType("application/json") .with(csrf())) - .andExpect(status().isForbidden()); + .andExpect(status().isOk()); } @Test @@ -86,11 +86,12 @@ void questionNegativeIdShouldReturn400() throws Exception{ } @Test - void answerQuestionShouldReturn403() throws Exception{ - mockMvc.perform(get("/questions/1/answer") + void answerQuestionNoAuthShouldReturn200() throws Exception{ + mockMvc.perform(post("/questions/1/answer") + .content(asJsonString(new AnswerDto(1L))) .contentType("application/json") .with(csrf())) - .andExpect(status().isForbidden()); + .andExpect(status().isOk()); } @Test From 5e0f61eef1087d36c3510f995e910984082ed7e3 Mon Sep 17 00:00:00 2001 From: sergioqfeg1 Date: Wed, 17 Apr 2024 19:38:07 +0200 Subject: [PATCH 15/30] feat: getQuestions controller --- .../questions/question/QuestionController.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/api/src/main/java/lab/en2b/quizapi/questions/question/QuestionController.java b/api/src/main/java/lab/en2b/quizapi/questions/question/QuestionController.java index c25d22b0..7bca7ff4 100644 --- a/api/src/main/java/lab/en2b/quizapi/questions/question/QuestionController.java +++ b/api/src/main/java/lab/en2b/quizapi/questions/question/QuestionController.java @@ -1,6 +1,7 @@ package lab.en2b.quizapi.questions.question; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.validation.Valid; @@ -12,6 +13,8 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import java.util.List; + @RestController @RequestMapping("/questions") @RequiredArgsConstructor @@ -47,4 +50,16 @@ private ResponseEntity generateQuestion(@RequestParam(requi private ResponseEntity getQuestionById(@PathVariable @PositiveOrZero Long id){ return ResponseEntity.ok(questionService.getQuestionById(id)); } + + @Operation(summary = "Gets a list of questions", description = "Gets a list of questions given a page number") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Successfully retrieved"), + @ApiResponse(responseCode = "404", description = "Not found - There are no questions", content = @io.swagger.v3.oas.annotations.media.Content), + @ApiResponse(responseCode = "400", description = "Bad request - The page number is invalid", content = @io.swagger.v3.oas.annotations.media.Content) + }) + @Parameter(name = "page", description = "The page number", required = true) + @GetMapping + private ResponseEntity> getQuestions(@RequestParam Long page){ + return ResponseEntity.ok(questionService.getQuestionsWithPage(page)); + } } From 68cccf868a3fecc27c74bf82c21730782b2c2b65 Mon Sep 17 00:00:00 2001 From: sergioqfeg1 Date: Wed, 17 Apr 2024 19:38:23 +0200 Subject: [PATCH 16/30] feat: getQuestions service --- .../en2b/quizapi/questions/question/QuestionService.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/api/src/main/java/lab/en2b/quizapi/questions/question/QuestionService.java b/api/src/main/java/lab/en2b/quizapi/questions/question/QuestionService.java index e0c420e4..6af96f66 100644 --- a/api/src/main/java/lab/en2b/quizapi/questions/question/QuestionService.java +++ b/api/src/main/java/lab/en2b/quizapi/questions/question/QuestionService.java @@ -69,6 +69,15 @@ public QuestionResponseDto getQuestionById(Long id) { return questionResponseDtoMapper.apply(q); } + /** + * Get a list of questions with a page + * @param page The page number + * @return the list of questions + */ + public List getQuestionsWithPage(Long page){ + return questionRepository.findAll().stream().map(questionResponseDtoMapper). + toList().subList(Math.toIntExact((page-1)*100), Math.toIntExact(page*100)); + } /** * Load the answers for a question (The distractors and the correct one) From 5029cfa024ebd5ceb81c2e8e1aca239eafa07c8c Mon Sep 17 00:00:00 2001 From: sergioqfeg1 Date: Wed, 17 Apr 2024 19:42:12 +0200 Subject: [PATCH 17/30] test: controller tests (200) --- .../questions/QuestionControllerTest.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/api/src/test/java/lab/en2b/quizapi/questions/QuestionControllerTest.java b/api/src/test/java/lab/en2b/quizapi/questions/QuestionControllerTest.java index e175a517..5544797c 100644 --- a/api/src/test/java/lab/en2b/quizapi/questions/QuestionControllerTest.java +++ b/api/src/test/java/lab/en2b/quizapi/questions/QuestionControllerTest.java @@ -138,4 +138,21 @@ void answerQuestionNegativeIdShouldReturn400() throws Exception{ .with(csrf())) .andExpect(status().isBadRequest()); } + + @Test + void getQuestionsWithPageNoAuthShouldReturn200() throws Exception{ + mockMvc.perform(get("/questions?page=1") + .contentType("application/json") + .with(csrf())) + .andExpect(status().isOk()); + } + + @Test + void getQuestionsWithPageShouldReturn200() throws Exception{ + mockMvc.perform(get("/questions?page=1") + .with(user("test").roles("user")) + .contentType("application/json") + .with(csrf())) + .andExpect(status().isOk()); + } } From 5f0155bd05d755bfafbb01612a3cb99b3efd1b04 Mon Sep 17 00:00:00 2001 From: sergioqfeg1 Date: Wed, 17 Apr 2024 19:44:21 +0200 Subject: [PATCH 18/30] test: controller tests (400) --- .../en2b/quizapi/questions/QuestionControllerTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/api/src/test/java/lab/en2b/quizapi/questions/QuestionControllerTest.java b/api/src/test/java/lab/en2b/quizapi/questions/QuestionControllerTest.java index 5544797c..ead2efc9 100644 --- a/api/src/test/java/lab/en2b/quizapi/questions/QuestionControllerTest.java +++ b/api/src/test/java/lab/en2b/quizapi/questions/QuestionControllerTest.java @@ -155,4 +155,13 @@ void getQuestionsWithPageShouldReturn200() throws Exception{ .with(csrf())) .andExpect(status().isOk()); } + + @Test + void getQuestionsWithPageNoPageShouldReturn400() throws Exception{ + mockMvc.perform(get("/questions") + .with(user("test").roles("user")) + .contentType("application/json") + .with(csrf())) + .andExpect(status().isBadRequest()); + } } From d59871902fc778cf687cc51f1ce7e8fd8e30c66e Mon Sep 17 00:00:00 2001 From: sergioqfeg1 Date: Wed, 17 Apr 2024 20:01:48 +0200 Subject: [PATCH 19/30] fix: getQuestions method needs to take into account other sizes --- .../en2b/quizapi/questions/question/QuestionService.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/lab/en2b/quizapi/questions/question/QuestionService.java b/api/src/main/java/lab/en2b/quizapi/questions/question/QuestionService.java index 6af96f66..c583c223 100644 --- a/api/src/main/java/lab/en2b/quizapi/questions/question/QuestionService.java +++ b/api/src/main/java/lab/en2b/quizapi/questions/question/QuestionService.java @@ -75,8 +75,13 @@ public QuestionResponseDto getQuestionById(Long id) { * @return the list of questions */ public List getQuestionsWithPage(Long page){ - return questionRepository.findAll().stream().map(questionResponseDtoMapper). - toList().subList(Math.toIntExact((page-1)*100), Math.toIntExact(page*100)); + if (page < 1) + throw new InternalApiErrorException("Invalid page number"); + List result = questionRepository.findAll().stream() + .map(questionResponseDtoMapper).toList(); + if (result.size() < page*100) + return result.subList(Math.toIntExact((page-1)*100),result.size()); + return result.subList(Math.toIntExact((page-1)*100), Math.toIntExact(page*100)); } /** From 7f06937957584053585f095b277bc7b56ca197a0 Mon Sep 17 00:00:00 2001 From: sergioqfeg1 Date: Wed, 17 Apr 2024 20:02:50 +0200 Subject: [PATCH 20/30] test: getQuestionsWithPage --- .../lab/en2b/quizapi/questions/QuestionServiceTest.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/api/src/test/java/lab/en2b/quizapi/questions/QuestionServiceTest.java b/api/src/test/java/lab/en2b/quizapi/questions/QuestionServiceTest.java index 3f30fc88..e883368d 100644 --- a/api/src/test/java/lab/en2b/quizapi/questions/QuestionServiceTest.java +++ b/api/src/test/java/lab/en2b/quizapi/questions/QuestionServiceTest.java @@ -175,5 +175,12 @@ void testAnswerQuestionInvalidAnswer(){ assertThrows(IllegalArgumentException.class,() -> questionService.answerQuestion(1L, AnswerDto.builder().answerId(3L).build())); } + @Test + void getQuestionsWithPage() { + when(questionRepository.findAll()).thenReturn(List.of(defaultQuestion)); + List response = questionService.getQuestionsWithPage(1L); + assertEquals(response, List.of(defaultResponseDto)); + } + } From 40f7e0116a962e7d05dbab45805ab9f3fc8d9bd8 Mon Sep 17 00:00:00 2001 From: sergioqfeg1 Date: Wed, 17 Apr 2024 20:05:03 +0200 Subject: [PATCH 21/30] test: getQuestionsWithInvalidPage + getQuestionsWithNoQuestions --- .../en2b/quizapi/questions/QuestionServiceTest.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/api/src/test/java/lab/en2b/quizapi/questions/QuestionServiceTest.java b/api/src/test/java/lab/en2b/quizapi/questions/QuestionServiceTest.java index e883368d..0c2f17a5 100644 --- a/api/src/test/java/lab/en2b/quizapi/questions/QuestionServiceTest.java +++ b/api/src/test/java/lab/en2b/quizapi/questions/QuestionServiceTest.java @@ -10,6 +10,7 @@ import lab.en2b.quizapi.questions.question.dtos.AnswerCheckResponseDto; import lab.en2b.quizapi.questions.question.dtos.QuestionResponseDto; import lab.en2b.quizapi.questions.question.mappers.QuestionResponseDtoMapper; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -182,5 +183,15 @@ void getQuestionsWithPage() { assertEquals(response, List.of(defaultResponseDto)); } + @Test + void getQuestionsWithPageInvalidPage() { + assertThrows(InternalApiErrorException.class,() -> questionService.getQuestionsWithPage(0L)); + } + + @Test + void getQuestionsWithPageNoQuestions() { + when(questionRepository.findAll()).thenReturn(List.of()); + Assertions.assertEquals(questionService.getQuestionsWithPage(1L), List.of()); + } } From 8f840df83faf85eeb81611a83fb0801edf5c18d1 Mon Sep 17 00:00:00 2001 From: Dario Date: Wed, 17 Apr 2024 20:12:27 +0200 Subject: [PATCH 22/30] fix: pattern breaking when running app --- .../main/java/lab/en2b/quizapi/auth/config/SecurityConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/lab/en2b/quizapi/auth/config/SecurityConfig.java b/api/src/main/java/lab/en2b/quizapi/auth/config/SecurityConfig.java index 2d6848a0..aa965a08 100644 --- a/api/src/main/java/lab/en2b/quizapi/auth/config/SecurityConfig.java +++ b/api/src/main/java/lab/en2b/quizapi/auth/config/SecurityConfig.java @@ -58,7 +58,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http, Authentication .cors(Customizer.withDefaults()) .sessionManagement(configuration -> configuration.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(authorize -> authorize - .requestMatchers(HttpMethod.POST,"/questions/**/answer").permitAll() + .requestMatchers(HttpMethod.POST,"/questions/**").permitAll() .requestMatchers(HttpMethod.GET,"/questions/**").permitAll() .requestMatchers(HttpMethod.GET,"/users/details").authenticated() .requestMatchers(HttpMethod.GET,"/users","/users/**").permitAll() From a4e67d4feac78d6ab6c3a0576c9d793808b23e16 Mon Sep 17 00:00:00 2001 From: Dario Date: Wed, 17 Apr 2024 20:43:31 +0200 Subject: [PATCH 23/30] fix: now checking if initial page is greater than size --- .../questions/question/QuestionController.java | 2 +- .../questions/question/QuestionService.java | 16 ++++++++++++---- .../quizapi/questions/QuestionServiceTest.java | 8 +++++++- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/api/src/main/java/lab/en2b/quizapi/questions/question/QuestionController.java b/api/src/main/java/lab/en2b/quizapi/questions/question/QuestionController.java index 7bca7ff4..b726e14d 100644 --- a/api/src/main/java/lab/en2b/quizapi/questions/question/QuestionController.java +++ b/api/src/main/java/lab/en2b/quizapi/questions/question/QuestionController.java @@ -57,7 +57,7 @@ private ResponseEntity getQuestionById(@PathVariable @Posit @ApiResponse(responseCode = "404", description = "Not found - There are no questions", content = @io.swagger.v3.oas.annotations.media.Content), @ApiResponse(responseCode = "400", description = "Bad request - The page number is invalid", content = @io.swagger.v3.oas.annotations.media.Content) }) - @Parameter(name = "page", description = "The page number", required = true) + @Parameter(name = "page", description = "The page number. Cannot be lower or equal to 0.", required = true) @GetMapping private ResponseEntity> getQuestions(@RequestParam Long page){ return ResponseEntity.ok(questionService.getQuestionsWithPage(page)); diff --git a/api/src/main/java/lab/en2b/quizapi/questions/question/QuestionService.java b/api/src/main/java/lab/en2b/quizapi/questions/question/QuestionService.java index c583c223..6e9bc1ac 100644 --- a/api/src/main/java/lab/en2b/quizapi/questions/question/QuestionService.java +++ b/api/src/main/java/lab/en2b/quizapi/questions/question/QuestionService.java @@ -76,12 +76,20 @@ public QuestionResponseDto getQuestionById(Long id) { */ public List getQuestionsWithPage(Long page){ if (page < 1) - throw new InternalApiErrorException("Invalid page number"); + throw new IllegalArgumentException("Invalid page number"); List result = questionRepository.findAll().stream() .map(questionResponseDtoMapper).toList(); - if (result.size() < page*100) - return result.subList(Math.toIntExact((page-1)*100),result.size()); - return result.subList(Math.toIntExact((page-1)*100), Math.toIntExact(page*100)); + return getPage(result, page); + } + + private List getPage(List result, Long page) { + int QUESTION_PAGE_SIZE = 100; + int startIndex = Math.toIntExact((page-1)* QUESTION_PAGE_SIZE); + if(startIndex > result.size()) + throw new IllegalArgumentException("Invalid page number, maximum page is "+(result.size()/ QUESTION_PAGE_SIZE +1) + " and you requested page "+page); + if (result.size() < page* QUESTION_PAGE_SIZE) + return result.subList(startIndex,result.size()); + return result.subList(startIndex, Math.toIntExact(page* QUESTION_PAGE_SIZE)); } /** diff --git a/api/src/test/java/lab/en2b/quizapi/questions/QuestionServiceTest.java b/api/src/test/java/lab/en2b/quizapi/questions/QuestionServiceTest.java index 0c2f17a5..a773740c 100644 --- a/api/src/test/java/lab/en2b/quizapi/questions/QuestionServiceTest.java +++ b/api/src/test/java/lab/en2b/quizapi/questions/QuestionServiceTest.java @@ -185,7 +185,13 @@ void getQuestionsWithPage() { @Test void getQuestionsWithPageInvalidPage() { - assertThrows(InternalApiErrorException.class,() -> questionService.getQuestionsWithPage(0L)); + assertThrows(IllegalArgumentException.class,() -> questionService.getQuestionsWithPage(0L)); + } + @Test + void getQuestionsWithPageGreaterThanSize() { + when(questionRepository.findAll()).thenReturn(List.of(defaultQuestion)); + + assertThrows(IllegalArgumentException.class,() -> questionService.getQuestionsWithPage(2L)); } @Test From e78504dd13295be87a39979fdd1d7b9e1c80480c Mon Sep 17 00:00:00 2001 From: Dario Date: Wed, 17 Apr 2024 20:46:54 +0200 Subject: [PATCH 24/30] fix:check for integer overflow --- .../questions/question/QuestionService.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/api/src/main/java/lab/en2b/quizapi/questions/question/QuestionService.java b/api/src/main/java/lab/en2b/quizapi/questions/question/QuestionService.java index 6e9bc1ac..093a1368 100644 --- a/api/src/main/java/lab/en2b/quizapi/questions/question/QuestionService.java +++ b/api/src/main/java/lab/en2b/quizapi/questions/question/QuestionService.java @@ -83,13 +83,18 @@ public List getQuestionsWithPage(Long page){ } private List getPage(List result, Long page) { - int QUESTION_PAGE_SIZE = 100; - int startIndex = Math.toIntExact((page-1)* QUESTION_PAGE_SIZE); - if(startIndex > result.size()) - throw new IllegalArgumentException("Invalid page number, maximum page is "+(result.size()/ QUESTION_PAGE_SIZE +1) + " and you requested page "+page); - if (result.size() < page* QUESTION_PAGE_SIZE) - return result.subList(startIndex,result.size()); - return result.subList(startIndex, Math.toIntExact(page* QUESTION_PAGE_SIZE)); + try{ + int QUESTION_PAGE_SIZE = 100; + int startIndex = Math.toIntExact((page-1)* QUESTION_PAGE_SIZE); + if(startIndex > result.size()) + throw new IllegalArgumentException("Invalid page number, maximum page is "+(result.size()/ QUESTION_PAGE_SIZE +1) + " and you requested page "+page); + if (result.size() < page* QUESTION_PAGE_SIZE) + return result.subList(startIndex,result.size()); + return result.subList(startIndex, Math.toIntExact(page* QUESTION_PAGE_SIZE)); + } catch (ArithmeticException e) { + throw new IllegalArgumentException("Invalid page number"); + } + } /** From c81b41dbdb7719126ce07c0b552f7edec6b2804b Mon Sep 17 00:00:00 2001 From: jjgancfer Date: Wed, 17 Apr 2024 23:57:18 +0200 Subject: [PATCH 25/30] fix: missing SELECT from query --- api/src/main/java/lab/en2b/quizapi/game/GameRepository.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/src/main/java/lab/en2b/quizapi/game/GameRepository.java b/api/src/main/java/lab/en2b/quizapi/game/GameRepository.java index 6f747994..13602f47 100644 --- a/api/src/main/java/lab/en2b/quizapi/game/GameRepository.java +++ b/api/src/main/java/lab/en2b/quizapi/game/GameRepository.java @@ -14,6 +14,7 @@ public interface GameRepository extends JpaRepository { @Query(value = "SELECT * FROM Games g WHERE user_id = ?1 AND g.is_game_over = false LIMIT 1", nativeQuery = true) Optional findActiveGameForUser(Long userId); - @Query(value = "COUNT(*) FROM Games g WHERE user_id = ?1 AND g.is_game_over = true", nativeQuery = true) + @Query(value = "SELECT COUNT(*) FROM Games g WHERE user_id = ?1 AND g" + + ".is_game_over = true", nativeQuery = true) Long countFinishedGamesForUser(Long userId); } From 304e9901d4c73b9a975a2387fd54d7d486a0bb52 Mon Sep 17 00:00:00 2001 From: Dario Date: Thu, 18 Apr 2024 09:53:24 +0200 Subject: [PATCH 26/30] feat: question cat has a proper dto --- .../quizapi/commons/utils/GameModeUtils.java | 68 +++++++++++++++++++ .../lab/en2b/quizapi/game/GameController.java | 5 +- .../lab/en2b/quizapi/game/GameService.java | 6 +- .../question/dtos/QuestionCategoryDto.java | 21 ++++++ .../en2b/quizapi/game/GameServiceTest.java | 2 +- 5 files changed, 97 insertions(+), 5 deletions(-) create mode 100644 api/src/main/java/lab/en2b/quizapi/questions/question/dtos/QuestionCategoryDto.java diff --git a/api/src/main/java/lab/en2b/quizapi/commons/utils/GameModeUtils.java b/api/src/main/java/lab/en2b/quizapi/commons/utils/GameModeUtils.java index 4e6c2886..eaf16af9 100644 --- a/api/src/main/java/lab/en2b/quizapi/commons/utils/GameModeUtils.java +++ b/api/src/main/java/lab/en2b/quizapi/commons/utils/GameModeUtils.java @@ -3,6 +3,7 @@ import lab.en2b.quizapi.game.Game; import lab.en2b.quizapi.game.GameMode; import lab.en2b.quizapi.questions.question.QuestionCategory; +import lab.en2b.quizapi.questions.question.dtos.QuestionCategoryDto; import java.util.List; @@ -54,4 +55,71 @@ public static void setGamemodeParams(Game game){ game.setRoundDuration(30); } } + + public static List getQuestionCategories(String lang) { + if(lang == null) + lang = "en"; + if(lang.equals("en")) + return getQuestionCategoriesEn(); + return getQuestionCategoriesEs(); + } + private static List getQuestionCategoriesEn(){ + return List.of( + QuestionCategoryDto.builder() + .name("Art") + .description("Are you an art expert? Prove it!") + .internalRepresentation(QuestionCategory.ART) + .build(), + QuestionCategoryDto.builder() + .name("Music") + .description("Are you a music lover? Prove it!") + .internalRepresentation(QuestionCategory.MUSIC) + .build(), + QuestionCategoryDto.builder() + .name("Geography") + .description("Are you a geography expert? Prove it!") + .internalRepresentation(QuestionCategory.GEOGRAPHY) + .build(), + QuestionCategoryDto.builder() + .name("Sports") + .description("Are you a sports fanatic? Prove it!") + .internalRepresentation(QuestionCategory.SPORTS) + .build(), + QuestionCategoryDto.builder() + .name("Video Games") + .description("Are you a gamer? Prove it!") + .internalRepresentation(QuestionCategory.VIDEOGAMES) + .build() + ); + } + + private static List getQuestionCategoriesEs(){ + return List.of( + QuestionCategoryDto.builder() + .name("Arte") + .description("¿Eres un experto en arte? ¡Demuéstralo!") + .internalRepresentation(QuestionCategory.ART) + .build(), + QuestionCategoryDto.builder() + .name("Música") + .description("¿Eres un melómano? ¡Demuéstralo!") + .internalRepresentation(QuestionCategory.MUSIC) + .build(), + QuestionCategoryDto.builder() + .name("Geografía") + .description("¿Eres un experto en geografía? ¡Demuéstralo!") + .internalRepresentation(QuestionCategory.GEOGRAPHY) + .build(), + QuestionCategoryDto.builder() + .name("Deportes") + .description("¿Eres un fanático de los deportes? ¡Demuéstralo!") + .internalRepresentation(QuestionCategory.SPORTS) + .build(), + QuestionCategoryDto.builder() + .name("Videojuegos") + .description("¿Eres un gamer? ¡Demuéstralo!") + .internalRepresentation(QuestionCategory.VIDEOGAMES) + .build() + ); + } } diff --git a/api/src/main/java/lab/en2b/quizapi/game/GameController.java b/api/src/main/java/lab/en2b/quizapi/game/GameController.java index 3192b857..99c1d872 100644 --- a/api/src/main/java/lab/en2b/quizapi/game/GameController.java +++ b/api/src/main/java/lab/en2b/quizapi/game/GameController.java @@ -8,6 +8,7 @@ import jakarta.validation.Valid; import lab.en2b.quizapi.game.dtos.*; import lab.en2b.quizapi.questions.question.QuestionCategory; +import lab.en2b.quizapi.questions.question.dtos.QuestionCategoryDto; import lab.en2b.quizapi.questions.question.dtos.QuestionResponseDto; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; @@ -133,8 +134,8 @@ public ResponseEntity> getQuestionGameModes(){ @ApiResponse(responseCode = "403", description = "You are not logged in", content = @io.swagger.v3.oas.annotations.media.Content) }) @GetMapping("/question-categories") - public ResponseEntity> getQuestionCategories(){ - return ResponseEntity.ok(gameService.getQuestionCategories()); + public ResponseEntity> getQuestionCategories(@RequestParam(required = false) String lang){ + return ResponseEntity.ok(gameService.getQuestionCategories(lang)); } } diff --git a/api/src/main/java/lab/en2b/quizapi/game/GameService.java b/api/src/main/java/lab/en2b/quizapi/game/GameService.java index 54eb2a9e..36b9d8a1 100644 --- a/api/src/main/java/lab/en2b/quizapi/game/GameService.java +++ b/api/src/main/java/lab/en2b/quizapi/game/GameService.java @@ -1,10 +1,12 @@ package lab.en2b.quizapi.game; import lab.en2b.quizapi.commons.user.UserService; +import lab.en2b.quizapi.commons.utils.GameModeUtils; import lab.en2b.quizapi.game.dtos.*; import lab.en2b.quizapi.game.mappers.GameResponseDtoMapper; import lab.en2b.quizapi.questions.question.QuestionCategory; import lab.en2b.quizapi.questions.question.QuestionService; +import lab.en2b.quizapi.questions.question.dtos.QuestionCategoryDto; import lab.en2b.quizapi.questions.question.dtos.QuestionResponseDto; import lab.en2b.quizapi.questions.question.mappers.QuestionResponseDtoMapper; import lab.en2b.quizapi.statistics.Statistics; @@ -151,8 +153,8 @@ public GameResponseDto getGameDetails(Long id, Authentication authentication) { return gameResponseDtoMapper.apply(game); } - public List getQuestionCategories() { - return Arrays.asList(QuestionCategory.values()); + public List getQuestionCategories(String lang) { + return GameModeUtils.getQuestionCategories(lang); } private boolean wasGameMeantToBeOver(Game game) { diff --git a/api/src/main/java/lab/en2b/quizapi/questions/question/dtos/QuestionCategoryDto.java b/api/src/main/java/lab/en2b/quizapi/questions/question/dtos/QuestionCategoryDto.java new file mode 100644 index 00000000..3d9180b4 --- /dev/null +++ b/api/src/main/java/lab/en2b/quizapi/questions/question/dtos/QuestionCategoryDto.java @@ -0,0 +1,21 @@ +package lab.en2b.quizapi.questions.question.dtos; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lab.en2b.quizapi.questions.question.QuestionCategory; +import lombok.*; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Setter +public class QuestionCategoryDto { + @Schema(description = "Beautified name of the question category",example = "Sports") + private String name; + @Schema(description = "Description of the question category",example = "Test description of the question category") + private String description; + @JsonProperty("internal_representation") + @Schema(description = "Internal code used for describing the question category",example = "SPORTS") + private QuestionCategory internalRepresentation; +} diff --git a/api/src/test/java/lab/en2b/quizapi/game/GameServiceTest.java b/api/src/test/java/lab/en2b/quizapi/game/GameServiceTest.java index 5063486b..ef3d483a 100644 --- a/api/src/test/java/lab/en2b/quizapi/game/GameServiceTest.java +++ b/api/src/test/java/lab/en2b/quizapi/game/GameServiceTest.java @@ -468,7 +468,7 @@ public void getGameDetailsInvalidId(){ @Test public void testGetQuestionCategories(){ - assertEquals(Arrays.asList(QuestionCategory.values()), gameService.getQuestionCategories()); + assertEquals(QuestionCategory.values().length, gameService.getQuestionCategories(null).size()); } @Test From 4465a5ff80ca41e78fa10140e8727134c0307615 Mon Sep 17 00:00:00 2001 From: Dario Date: Thu, 18 Apr 2024 10:54:05 +0200 Subject: [PATCH 27/30] feat: graphana and docker now use own docker image --- .github/workflows/release.yml | 13 +++++++++++++ docker-compose.yml | 33 ++++++++++++++++++--------------- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e698e310..5336b788 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,6 +65,19 @@ jobs: DATABASE_PASSWORD JWT_SECRET SSL_PASSWORD + docker-push-prometheus: + runs-on: ubuntu-latest + needs: [ e2e-tests ] + steps: + - uses: actions/checkout@v4 + - name: Publish to Registry + uses: elgohr/Publish-Docker-Github-Action@v5 + with: + name: arquisoft/wiq_en2b/prometheus + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + registry: ghcr.io + workdir: api/monitoring/prometheus docker-push-kiwiq: runs-on: ubuntu-latest needs: [ e2e-tests ] diff --git a/docker-compose.yml b/docker-compose.yml index 8fa734aa..e587ec0a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -57,19 +57,6 @@ depends_on: - WIQ_DB - prometheus: - image: prom/prometheus - container_name: prometheus-${teamname:-defaultASW} - profiles: ["dev", "prod"] - networks: - mynetwork: - volumes: - - ./quiz-api/monitoring/prometheus:/etc/prometheus - - prometheus_data:/prometheus - - /certs:/etc/letsencrypt/kiwiq.run.place:ro - depends_on: - - api - kiwiq: image: ghcr.io/arquisoft/wiq_en2b/kiwiq:latest container_name: kiwiq @@ -99,16 +86,32 @@ - REACT_APP_API_ENDPOINT=${API_URI} networks: mynetwork: + + prometheus: + image: ghcr.io/arquisoft/wiq_en2b/prometheus:latest + container_name: prometheus-${teamname:-defaultASW} + profiles: ["dev", "prod"] + ports: + - "9090:9090" + networks: + mynetwork: + volumes: + - prometheus_data:/prometheus + - /certs:/etc/letsencrypt/kiwiq.run.place:ro + depends_on: + - api grafana: - image: grafana/grafana + image: ghcr.io/arquisoft/wiq_en2b/grafana:latest container_name: grafana-${teamname:-defaultASW} profiles: [ "dev" , "prod"] networks: mynetwork: volumes: - grafana_data:/var/lib/grafana - - ./quiz-api/monitoring/grafana/provisioning:/etc/grafana/provisioning + - /certs:/etc/letsencrypt/kiwiq.run.place:ro + ports: + - "9091:9091" environment: - GF_SERVER_HTTP_PORT=9091 - GF_AUTH_DISABLE_LOGIN_FORM=true From 921e1657ebe5ce5746fffcddf8cdf0a600f5c537 Mon Sep 17 00:00:00 2001 From: Dario Date: Thu, 18 Apr 2024 11:11:37 +0200 Subject: [PATCH 28/30] feat: dockerfiles for grafana and prometheus --- api/monitoring/grafana/Dockerfile | 14 ++++++++++++++ api/monitoring/prometheus/Dockerfile | 14 ++++++++++++++ .../prometheus/{ => configuration}/prometheus.yml | 0 3 files changed, 28 insertions(+) create mode 100644 api/monitoring/grafana/Dockerfile create mode 100644 api/monitoring/prometheus/Dockerfile rename api/monitoring/prometheus/{ => configuration}/prometheus.yml (100%) diff --git a/api/monitoring/grafana/Dockerfile b/api/monitoring/grafana/Dockerfile new file mode 100644 index 00000000..624fc546 --- /dev/null +++ b/api/monitoring/grafana/Dockerfile @@ -0,0 +1,14 @@ +FROM grafana/grafana +LABEL authors="dario" +# Define the source and destination directories +COPY_SOURCE = ./provisioning +COPY_DESTINATION = /etc/grafana/provisioning + +# Copy the configuration files +COPY ${COPY_SOURCE}/* ${COPY_DESTINATION} + +# Expose the default Grafana port +EXPOSE 9091 + +# Run Grafana in the foreground +CMD ["grafana-server"] \ No newline at end of file diff --git a/api/monitoring/prometheus/Dockerfile b/api/monitoring/prometheus/Dockerfile new file mode 100644 index 00000000..13f90329 --- /dev/null +++ b/api/monitoring/prometheus/Dockerfile @@ -0,0 +1,14 @@ +FROM prom/prometheus +LABEL authors="dario" +# Define the source and destination directories +COPY_SOURCE = ./configuration +COPY_DESTINATION = /etc/prometheus + +# Copy the configuration files +COPY ${COPY_SOURCE}/* ${COPY_DESTINATION} + +# Expose the default Prometheus port +EXPOSE 9090 + +# Run Prometheus in the foreground +CMD ["prometheus"] diff --git a/api/monitoring/prometheus/prometheus.yml b/api/monitoring/prometheus/configuration/prometheus.yml similarity index 100% rename from api/monitoring/prometheus/prometheus.yml rename to api/monitoring/prometheus/configuration/prometheus.yml From f4cd1e968ce002bec4087efe4ae536b715dd651d Mon Sep 17 00:00:00 2001 From: Dario Date: Thu, 18 Apr 2024 11:11:50 +0200 Subject: [PATCH 29/30] feat: grafana in release.yml --- .github/workflows/release.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5336b788..76fff00f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -78,6 +78,19 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} registry: ghcr.io workdir: api/monitoring/prometheus + docker-push-grafana: + runs-on: ubuntu-latest + needs: [ e2e-tests ] + steps: + - uses: actions/checkout@v4 + - name: Publish to Registry + uses: elgohr/Publish-Docker-Github-Action@v5 + with: + name: arquisoft/wiq_en2b/grafana + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + registry: ghcr.io + workdir: api/monitoring/grafana docker-push-kiwiq: runs-on: ubuntu-latest needs: [ e2e-tests ] From 2be4075a5715001204d1366933b6ba68f7eae43d Mon Sep 17 00:00:00 2001 From: Dario Date: Thu, 18 Apr 2024 11:15:56 +0200 Subject: [PATCH 30/30] fix: port changed to 8443 --- api/monitoring/prometheus/configuration/prometheus.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/monitoring/prometheus/configuration/prometheus.yml b/api/monitoring/prometheus/configuration/prometheus.yml index 8b0455fd..8a5f0486 100644 --- a/api/monitoring/prometheus/configuration/prometheus.yml +++ b/api/monitoring/prometheus/configuration/prometheus.yml @@ -3,6 +3,6 @@ scrape_configs: metrics_path: '/actuator/prometheus' scrape_interval: 10s static_configs: - - targets: ['host.docker.internal:8080'] + - targets: ['host.docker.internal:8443'] labels: application: 'WIQ API' \ No newline at end of file