diff --git a/api/api.yml b/api/api.yml index 17a79e2..74ca498 100644 --- a/api/api.yml +++ b/api/api.yml @@ -58,6 +58,17 @@ components: type: string required: - username + ############################################# + # PlayerApiModel # + ############################################# + PlayerApiModel: + description: a Player + type: object + properties: + username: + type: string + required: + - username responses: 201PlayerCreated: description: "201 - Player Created" @@ -146,5 +157,63 @@ paths: $ref: "#/components/responses/401Unauthorized" 500: $ref: "#/components/responses/500InternalError" + 503: + $ref: "#/components/responses/503ServiceUnavailable" + /admin/players: + get: + operationId: "GetAllPlayers" + summary: "Retrieve a paginated list of players" + description: "Returns a paginated list of players, optionally filtered by username." + parameters: + - name: page + in: query + description: "Page number (zero-based index)." + required: false + schema: + type: integer + default: 0 + - name: pageSize + in: query + description: "Number of players per page." + required: false + schema: + type: integer + default: 10 + - name: sortBy + in: query + description: "Field to sort by (default is username)." + required: false + schema: + type: string + default: "username" + - name: order + in: query + description: "Sorting order (asc or desc)." + required: false + schema: + type: string + enum: [asc, desc] + default: "asc" + - name: username + in: query + description: "Filter players by username (case-insensitive, partial match)." + required: false + schema: + type: string + tags: + - admin + responses: + 200: + description: "A List of all Player" + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/PlayerApiModel" + 401: + $ref: "#/components/responses/401Unauthorized" + 500: + $ref: "#/components/responses/500InternalError" 503: $ref: "#/components/responses/503ServiceUnavailable" \ No newline at end of file diff --git a/src/main/java/de/towerdefence/server/admin/AdminApiController.java b/src/main/java/de/towerdefence/server/admin/AdminApiController.java index a04f9b6..2be1f3f 100644 --- a/src/main/java/de/towerdefence/server/admin/AdminApiController.java +++ b/src/main/java/de/towerdefence/server/admin/AdminApiController.java @@ -1,16 +1,24 @@ package de.towerdefence.server.admin; - import com.fasterxml.jackson.databind.ObjectMapper; import de.towerdefence.server.auth.UserSession; import de.towerdefence.server.oas.AdminApi; import de.towerdefence.server.oas.models.AdminAuthInfo; +import de.towerdefence.server.oas.models.PlayerApiModel; +import de.towerdefence.server.player.Player; +import de.towerdefence.server.player.PlayerRepository; +import de.towerdefence.server.utils.PlayerMapperService; import jakarta.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; +import java.util.List; import java.util.Optional; @Controller @@ -20,6 +28,13 @@ public class AdminApiController implements AdminApi { @Autowired UserSession userSession; + @Autowired + PlayerRepository playerRepository; + + @Autowired + PlayerMapperService playerMapperService; + + @Override public Optional getObjectMapper() { return Optional.empty(); @@ -36,4 +51,20 @@ public class AdminApiController implements AdminApi { authInfo.setUsername(this.userSession.getUsername()); return ResponseEntity.ok(authInfo); } + + @Override + public ResponseEntity> getAllPlayers(Integer page, Integer pageSize, String sortBy, + String order, String username) { + + Sort.Direction direction = order.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; + Pageable pageable = PageRequest.of(page, pageSize, Sort.by(direction, sortBy)); + + Page playerPage = (username != null && !username.trim().isEmpty()) + ? playerRepository.findByUsernameContainingIgnoreCase(username, pageable) + : playerRepository.findAll(pageable); + + List playersMapped = playerMapperService.mapPlayerToApiPlayers(playerPage.getContent()); + + return playersMapped.isEmpty() ? ResponseEntity.noContent().build() : ResponseEntity.ok(playersMapped); + } } diff --git a/src/main/java/de/towerdefence/server/player/PlayerRepository.java b/src/main/java/de/towerdefence/server/player/PlayerRepository.java index 5ca4c19..612608c 100644 --- a/src/main/java/de/towerdefence/server/player/PlayerRepository.java +++ b/src/main/java/de/towerdefence/server/player/PlayerRepository.java @@ -1,8 +1,11 @@ package de.towerdefence.server.player; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; public interface PlayerRepository extends JpaRepository { Player findByUsername(String username); boolean existsByUsername(String username); + Page findByUsernameContainingIgnoreCase(String username, Pageable pageable); } diff --git a/src/main/java/de/towerdefence/server/utils/PlayerMapperService.java b/src/main/java/de/towerdefence/server/utils/PlayerMapperService.java new file mode 100644 index 0000000..da02c2c --- /dev/null +++ b/src/main/java/de/towerdefence/server/utils/PlayerMapperService.java @@ -0,0 +1,22 @@ +package de.towerdefence.server.utils; + +import de.towerdefence.server.oas.models.PlayerApiModel; +import de.towerdefence.server.player.Player; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +@Component +public class PlayerMapperService { + + public List mapPlayerToApiPlayers(List players) { + List apiPlayers = new ArrayList<>(); + for (Player player : players) { + PlayerApiModel apiPlayer = new PlayerApiModel(); + apiPlayer.setUsername(player.getUsername()); + apiPlayers.add(apiPlayer); + } + return apiPlayers; + } +} diff --git a/src/test/java/de/towerdefence/server/IntegrationTest.java b/src/test/java/de/towerdefence/server/IntegrationTest.java index 5964f75..75a7042 100644 --- a/src/test/java/de/towerdefence/server/IntegrationTest.java +++ b/src/test/java/de/towerdefence/server/IntegrationTest.java @@ -1,6 +1,7 @@ package de.towerdefence.server; import com.fasterxml.jackson.databind.ObjectMapper; +import de.towerdefence.server.player.Player; import de.towerdefence.server.player.PlayerRepository; import de.towerdefence.server.player.PlayerService; import org.junit.jupiter.api.AfterEach; @@ -11,6 +12,8 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.web.servlet.MockMvc; +import java.security.NoSuchAlgorithmException; + @SpringBootTest @AutoConfigureMockMvc(addFilters = false) @ActiveProfiles("test") @@ -24,10 +27,30 @@ public abstract class IntegrationTest { protected ObjectMapper objectMapper; @Autowired protected PlayerRepository playerRepository; - + @Autowired + protected PlayerService playerService; + @BeforeEach void setUp() { playerRepository.deleteAll(); + + Player player1 = new Player(); + player1.setUsername("Alex"); + try { + playerService.setPassword(player1, "1234"); + } catch (NoSuchAlgorithmException e) {return;} + + this.playerRepository.save(player1); + + Player player2 = new Player(); + player2.setUsername("Zorro"); + try { + playerService.setPassword(player2, "1234"); + } catch (NoSuchAlgorithmException e) {return;} + + this.playerRepository.save(player2); + + System.out.println("LISTE: " + playerRepository.findAll()); } @AfterEach diff --git a/src/test/java/de/towerdefence/server/server/GetAllPlayersPaginatedAndSortedTest.java b/src/test/java/de/towerdefence/server/server/GetAllPlayersPaginatedAndSortedTest.java new file mode 100644 index 0000000..f4835a8 --- /dev/null +++ b/src/test/java/de/towerdefence/server/server/GetAllPlayersPaginatedAndSortedTest.java @@ -0,0 +1,70 @@ +package de.towerdefence.server.server; + +import de.towerdefence.server.IntegrationTest; +import org.junit.jupiter.api.Test; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; + +class GetAllPlayersPaginatedAndSortedTest extends IntegrationTest { + + @Test + void playersExist() throws Exception { + //Testing if list ist being returned successfully + this.mvc.perform(MockMvcRequestBuilders.get(baseUri + "/admin/players") + .param("page", "0") + .param("pageSize", "10") + .param("sortBy", "username") + .param("order", "desc") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$").isArray()) + .andExpect(jsonPath("$[0]").exists()); + } + + @Test + void playersSortedByAsc() throws Exception { + //Test list ist being sorted correctly + this.mvc.perform(MockMvcRequestBuilders.get(baseUri + "/admin/players") + .param("page", "0") + .param("pageSize", "10") + .param("sortBy", "username") + .param("order", "asc") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0].username").value("Alex")) + .andExpect(jsonPath("$[1].username").value("Zorro")); + + + } + + @Test + void playersSortedByDesc() throws Exception { + //Test list ist being sorted correctly + this.mvc.perform(MockMvcRequestBuilders.get(baseUri + "/admin/players") + .param("page", "0") + .param("pageSize", "10") + .param("sortBy", "username") + .param("order", "desc") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$[1].username").value("Alex")) + .andExpect(jsonPath("$[0].username").value("Zorro")); + } + + @Test + void playersFiltered() throws Exception { + //testing if specific username that is being filtered by is in the result list + this.mvc.perform(MockMvcRequestBuilders.get(baseUri + "/admin/players") + .param("page", "0") + .param("pageSize", "10") + .param("username", "Alex") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$").isArray()) + .andExpect(jsonPath("$[0].username").value("Alex")) + .andExpect(jsonPath("$").isNotEmpty()); + } +} diff --git a/src/test/java/de/towerdefence/server/server/PlayerRegistrationTest.java b/src/test/java/de/towerdefence/server/server/PlayerRegistrationTest.java index 4a76286..df8a3ce 100644 --- a/src/test/java/de/towerdefence/server/server/PlayerRegistrationTest.java +++ b/src/test/java/de/towerdefence/server/server/PlayerRegistrationTest.java @@ -30,6 +30,4 @@ public class PlayerRegistrationTest extends IntegrationTest { .content(this.objectMapper.writeValueAsString(playerRegistrationData)) .contentType(MediaType.APPLICATION_JSON); } - - }