From e15ad419d093f8a416e355d512c1dfc9bc9df235 Mon Sep 17 00:00:00 2001
From: Mikkel Vestergaard <56039404+TehAwol@users.noreply.github.com>
Date: Sat, 27 Apr 2024 17:02:38 +0200
Subject: [PATCH 01/11] Add base paginated game query
---
.../java/com/wegas/core/ejb/GameFacade.java | 194 +++++++++++-------
.../com/wegas/core/rest/GameController.java | 37 +++-
2 files changed, 146 insertions(+), 85 deletions(-)
diff --git a/wegas-core/src/main/java/com/wegas/core/ejb/GameFacade.java b/wegas-core/src/main/java/com/wegas/core/ejb/GameFacade.java
index d5d4cc7f59..b4b9ffff7f 100644
--- a/wegas-core/src/main/java/com/wegas/core/ejb/GameFacade.java
+++ b/wegas-core/src/main/java/com/wegas/core/ejb/GameFacade.java
@@ -1,7 +1,7 @@
/**
* Wegas
* http://wegas.albasim.ch
- *
+ *
* Copyright (c) 2013-2021 School of Management and Engineering Vaud, Comem, MEI
* Licensed under the MIT License
*/
@@ -25,6 +25,8 @@
import com.wegas.core.persistence.game.Populatable.Status;
import com.wegas.core.persistence.game.Script;
import com.wegas.core.persistence.game.Team;
+import com.wegas.core.rest.util.pagination.Page;
+import com.wegas.core.rest.util.pagination.Pageable;
import com.wegas.core.security.ejb.AccountFacade;
import com.wegas.core.security.ejb.UserFacade;
import com.wegas.core.security.guest.GuestJpaAccount;
@@ -33,23 +35,22 @@
import com.wegas.core.security.persistence.token.SurveyToken;
import com.wegas.core.security.util.ScriptExecutionContext;
import com.wegas.survey.persistence.SurveyDescriptor;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
+
+import java.util.*;
import java.util.stream.Collectors;
+
import jakarta.ejb.LocalBean;
import jakarta.ejb.Stateless;
import jakarta.ejb.TransactionAttribute;
import jakarta.ejb.TransactionAttributeType;
import jakarta.enterprise.event.Event;
import jakarta.inject.Inject;
+
import javax.naming.NamingException;
+
import jakarta.persistence.NoResultException;
import jakarta.persistence.TypedQuery;
+import jakarta.persistence.criteria.*;
import jakarta.servlet.http.HttpServletRequest;
import org.openjdk.nashorn.api.scripting.ScriptObjectMirror;
import org.apache.poi.ss.usermodel.Cell;
@@ -148,9 +149,7 @@ public boolean isPersisted(final Long gameId) {
*
* @param gameModelId id of the gameModel to create a new game for
* @param game the game to persist
- *
* @throws java.lang.CloneNotSupportedException
- *
*/
public void publishAndCreate(final Long gameModelId, final Game game) throws CloneNotSupportedException {
GameModel gm = gameModelFacade.createPlayGameModel(gameModelId);
@@ -190,11 +189,11 @@ private void create(final GameModel gameModel, final Game game) {
final User currentUser = userFacade.getCurrentUser();
- if (Helper.isNullOrEmpty(game.getToken())){
+ if (Helper.isNullOrEmpty(game.getToken())) {
game.setToken(this.createUniqueToken(game));
} else if (this.findLiveOrBinByToken(game.getToken()) != null) {
throw WegasErrorMessage.error("This access key is already in use",
- "COMMONS-SESSIONS-TAKEN-TOKEN-ERROR");
+ "COMMONS-SESSIONS-TAKEN-TOKEN-ERROR");
}
getEntityManager().persist(game);
@@ -220,7 +219,6 @@ private void create(final GameModel gameModel, final Game game) {
* Add a debugteam within the game, unless such a team already exists
*
* @param game the game
- *
* @return true if the debug game has been added, false if it was already here
*/
public boolean addDebugTeam(Game game) {
@@ -247,7 +245,6 @@ public boolean addDebugTeam(Game game) {
/**
* @param game
- *
* @return a unique token based on the game name, suffixed with some random characters
*/
public String createUniqueToken(Game game) {
@@ -303,7 +300,7 @@ public void remove(final Game entity) {
// This is for retrocompatibility w/ game models that do not habe DebugGame
if (entity.getGameModel().getGames().size() <= 1
- && !(entity.getGameModel().getGames().get(0) instanceof DebugGame)) {// This is for retrocompatibility w/ game models that do not habe DebugGame
+ && !(entity.getGameModel().getGames().get(0) instanceof DebugGame)) {// This is for retrocompatibility w/ game models that do not habe DebugGame
gameModelFacade.remove(entity.getGameModel());
} else {
getEntityManager().remove(entity);
@@ -327,7 +324,6 @@ public void removeTX(Long gameId) {
* Search for a LIVE game with token
*
* @param token
- *
* @return first game found or null
*/
public Game findByToken(final String token) {
@@ -344,9 +340,9 @@ public Game findLiveOrBinByToken(final String token) {
public Game findByStatusAndToken(final String token, Game.Status status) {
final TypedQuery tq = getEntityManager()
- .createNamedQuery("Game.findByToken", Game.class)
- .setParameter("token", token)
- .setParameter("status", status);
+ .createNamedQuery("Game.findByToken", Game.class)
+ .setParameter("token", token)
+ .setParameter("status", status);
try {
return tq.getSingleResult();
} catch (NoResultException ex) {
@@ -356,7 +352,6 @@ public Game findByStatusAndToken(final String token, Game.Status status) {
/**
* @param search
- *
* @return all game matching the search token
*/
public List findByName(final String search) {
@@ -368,37 +363,103 @@ public List findByName(final String search) {
/**
* @param gameModelId
* @param orderBy not used...
- *
* @return all games belonging to the gameModel identified by gameModelId but DebugGames,
- * ordered by creation time
+ * ordered by creation time
*/
public List findByGameModelId(final Long gameModelId, final String orderBy) {
return getEntityManager().createQuery("SELECT g FROM Game g "
- + "WHERE TYPE(g) != DebugGame AND g.gameModel.id = :gameModelId ORDER BY g.createdTime DESC", Game.class)
- .setParameter("gameModelId", gameModelId)
- .getResultList();
+ + "WHERE TYPE(g) != DebugGame AND g.gameModel.id = :gameModelId ORDER BY g.createdTime DESC", Game.class)
+ .setParameter("gameModelId", gameModelId)
+ .getResultList();
}
/**
* @param status
- *
* @return all games which match the given status
*/
public List findAll(final Game.Status status) {
return getEntityManager().createNamedQuery("Game.findByStatus", Game.class)
- .setParameter("status", status).getResultList();
+ .setParameter("status", status).getResultList();
+ }
+
+// /**
+// * Find all games with given ids and status (was used for faster findByStatusAndUser fetch)
+// *
+// * @param ids ids of games to fetch
+// * @param status status of games to fetch
+// * @return all games which match given ids and status
+// */
+// public List findByIdsAndStatus(final List ids, final Game.Status status) {
+// final CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder();
+// final CriteriaQuery query = criteriaBuilder.createQuery(Game.class);
+// Root gameRoot = query.from(Game.class);
+//
+// query.where(
+// criteriaBuilder.and(
+// gameRoot.get("id").in(ids),
+// criteriaBuilder.equal(gameRoot.get("status"), status)
+// )
+// );
+//
+// TypedQuery typedQuery = getEntityManager().createQuery(query);
+//
+// return typedQuery.getResultList();
+// }
+
+ /**
+ * Get all paginated games with the given status which are accessible to the current user
+ *
+ * @param status status {@link Game.Status#LIVE} {@link Game.Status#BIN} {@link Game.Status#DELETE}
+ * @param pageable
+ * @return all games paginated
+ */
+ public Page findByStatusAndUserPaginated(Game.Status status, Pageable pageable) {
+
+ List gStatuses = new ArrayList<>();
+ gStatuses.add(status);
+
+ Map> gMatrix = this.getPermissionMatrix(gStatuses);
+ Map> filteredGMatrix = gMatrix.entrySet().stream()
+ .filter(l -> l.getValue().contains("Edit") || l.getValue().contains("*"))
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+
+ final CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder();
+ final CriteriaQuery query = criteriaBuilder.createQuery(Game.class);
+ Root gameRoot = query.from(Game.class);
+ query.select(gameRoot);
+
+
+ Predicate whereClause = criteriaBuilder.and(
+ criteriaBuilder.equal(gameRoot.get("status"), status),
+ gameRoot.get("id").in(new ArrayList<>(filteredGMatrix.keySet()))
+ );
+
+
+ if (pageable.getQuery() != null) {
+ String lowerCaseQuery = pageable.getQuery().toLowerCase();
+ Join gameModelJoin = gameRoot.join("gameModel");
+ whereClause = criteriaBuilder.and(whereClause, criteriaBuilder.or(
+ criteriaBuilder.like(criteriaBuilder.lower(gameRoot.get("name")), "%" + lowerCaseQuery + "%"),
+ criteriaBuilder.like(criteriaBuilder.lower(gameModelJoin.get("name")), "%" + lowerCaseQuery + "%")
+ ));
+ }
+ query.where(whereClause);
+
+ int total = getEntityManager().createQuery(query).getResultList().size();
+ TypedQuery listQuery = pageable.paginateQuery(getEntityManager().createQuery(query));
+
+ return new Page(total, pageable.getPage(), pageable.getSize(), listQuery.getResultList());
}
/**
* Find all game by status.
*
* @param statuses statuses to search
- *
* @return all games which match any of the given status
*/
public List findAll(final List statuses) {
return getEntityManager().createNamedQuery("Game.findByStatuses", Game.class)
- .setParameter("statuses", statuses).getResultList();
+ .setParameter("statuses", statuses).getResultList();
}
@@ -409,7 +470,7 @@ public List findAll(final List statuses) {
*/
public List getAllGameIdByLogId(String logId) {
TypedQuery query = this.getEntityManager()
- .createNamedQuery("Game.findAllIdByLogId", Long.class);
+ .createNamedQuery("Game.findAllIdByLogId", Long.class);
query.setParameter("logId", logId);
return query.getResultList();
}
@@ -418,15 +479,12 @@ public List getAllGameIdByLogId(String logId) {
* Get all games with the given status which are accessible to the current user
*
* @param status {@link Game.Status#LIVE} {@link Game.Status#BIN} {@link Game.Status#DELETE}
- *
* @return the list of all games which given status the current use has access to
*/
public Collection findByStatusAndUser(Game.Status status) {
List gStatuses = new ArrayList<>();
gStatuses.add(status);
- Map> gMatrix = this.getPermissionMatrix(gStatuses);
-
ArrayList games = new ArrayList<>();
for (Map.Entry> entry : gMatrix.entrySet()) {
@@ -447,7 +505,6 @@ public Collection findByStatusAndUser(Game.Status status) {
* Fetch all game ids that current user has access to which match any of the given statuses.
*
* @param statuses statuses of game to look for
- *
* @return list of gameId mapped to the permission the user has
*/
public Map> getPermissionMatrix(List statuses) {
@@ -457,9 +514,9 @@ public Map> getPermissionMatrix(List statuses) {
// it was time consuming
// New way is to fetch permissions first and extract games from this list
String roleQuery = "SELECT p FROM Permission p WHERE "
- + "(p.role.id in "
- + " (SELECT r.id FROM User u JOIN u.roles r WHERE u.id = :userId)"
- + ")";
+ + "(p.role.id in "
+ + " (SELECT r.id FROM User u JOIN u.roles r WHERE u.id = :userId)"
+ + ")";
String userQuery = "SELECT p FROM Permission p WHERE p.user.id = :userId";
@@ -475,7 +532,6 @@ public Map> getPermissionMatrix(List statuses) {
*
* @param game the game to join
* @param languages list of user preferred languages
- *
* @return the brand new player
*/
public Player joinIndividually(Game game, List languages) {
@@ -496,7 +552,6 @@ public Player joinIndividually(Game game, List languages) {
*
* @param game the game to join
* @param languages list of user preferred languages
- *
* @return the brand new player
*/
public Player joinForSurvey(Game game, List languages) {
@@ -526,25 +581,22 @@ public Player joinForSurvey(Game game, List languages) {
* Return all accounts with valid email address linked to LIVE players of the given game.
*
* @param game
- *
* @return
*/
private List getPlayerAccountsWithEmail(Game game) {
return game.getLivePlayers().stream()
- .map(p -> p.getUser())
- .filter(u -> u != null)
- .map(u -> u.getMainAccount())
- .filter(account -> account != null && !Helper.isNullOrEmpty(account.getEmail()))
- .collect(Collectors.toList());
+ .map(p -> p.getUser())
+ .filter(u -> u != null)
+ .map(u -> u.getMainAccount())
+ .filter(account -> account != null && !Helper.isNullOrEmpty(account.getEmail()))
+ .collect(Collectors.toList());
}
/**
* Get the one game from a list of surveys.
*
* @param surveys
- *
* @return the game
- *
* @throws WegasErrorMessage surveys belong to different games or no game found
*/
public Game getGameFromSurveys(List surveys) {
@@ -572,15 +624,13 @@ public Game getGameFromSurveys(List surveys) {
* @param surveys surveys
* @param request need request to generate the link
* @param email structure with attributes recipients (ignored here), sender, subject and body.
- *
* @return list of emails to which an invitation has been sent
- *
* @throws WegasErrorMessage if 1) surveys belong to different GameModels; 2) no game; 3) no
* account
*/
public List sendSurveysInvitation(List surveys,
- EmailAttributes email,
- HttpServletRequest request) {
+ EmailAttributes email,
+ HttpServletRequest request) {
Game game = getGameFromSurveys(surveys);
List accounts = getPlayerAccountsWithEmail(game);
List invitedEmails = new ArrayList<>();
@@ -599,12 +649,12 @@ public List sendSurveysInvitation(List surveys,
private List loadSurveys(String ids) {
return Arrays.stream(ids.split(","))
- .map(sId -> (SurveyDescriptor) variableDescriptorFacade.find(Long.parseLong(sId.trim())))
- .collect(Collectors.toList());
+ .map(sId -> (SurveyDescriptor) variableDescriptorFacade.find(Long.parseLong(sId.trim())))
+ .collect(Collectors.toList());
}
public List sendSurveysInvitationToPlayers(HttpServletRequest request,
- String surveyIds, EmailAttributes email) {
+ String surveyIds, EmailAttributes email) {
List surveys = this.loadSurveys(surveyIds);
@@ -612,7 +662,7 @@ public List sendSurveysInvitationToPlayers(HttpServletRequest request,
}
public List sendSurveysInvitationAnonymouslyToPlayers(HttpServletRequest request,
- String surveyIds, EmailAttributes email) {
+ String surveyIds, EmailAttributes email) {
List surveys = this.loadSurveys(surveyIds);
@@ -630,15 +680,13 @@ public List sendSurveysInvitationAnonymouslyToPlayers(HttpServletRequest
* @param surveys surveys
* @param email structure with attributes recipients (ignored here), sender, subject and body.
* @param request need request to generate the link
- *
* @return list of emails to which an invitation has been sent
- *
* @throws WegasErrorMessage if 1) surveys belong to different GameModel; 2) no game; 3) no
* account
*/
public List sendSurveysInvitationAnonymouslyToPlayers(List surveys,
- EmailAttributes email,
- HttpServletRequest request
+ EmailAttributes email,
+ HttpServletRequest request
) {
Game game = getGameFromSurveys(surveys);
List accounts = getPlayerAccountsWithEmail(game);
@@ -656,8 +704,8 @@ public List sendSurveysInvitationAnonymouslyToPlayers(List surveys = this.loadSurveys(surveyIds);
@@ -672,13 +720,12 @@ public void sendSurveysInvitationAnonymouslyToList(HttpServletRequest request,
* @param surveys surveys
* @param email structure with attributes recipients, sender, subject and body.
* @param request need request to generate the link
- *
* @throws WegasErrorMessage if 1) surveys belong to different GameModel; 2) no game; 3) no
* account
*/
public void sendSurveysInvitationAnonymouslyToList(List surveys,
- EmailAttributes email,
- HttpServletRequest request) {
+ EmailAttributes email,
+ HttpServletRequest request) {
if (!email.getRecipients().isEmpty()) {
accountFacade.sendSurveyAnonymousTokens(email, surveys, request);
@@ -694,7 +741,6 @@ public void sendSurveysInvitationAnonymouslyToList(List survey
* @param userId id of the user to create a player for, may be null to create an anonymous
* player
* @param languages
- *
* @return a new player, linked to a user, who just joined the team
*/
public Player joinTeam(Long teamId, Long userId, List languages) {
@@ -715,7 +761,6 @@ public Player joinTeam(Long teamId, Long userId, List languages) {
* (for testing purpose)
*
* @param teamId id of the team to join
- *
* @return a new player anonymous player who just joined the team
*/
public Player joinTeam(Long teamId, List languages) {
@@ -774,7 +819,6 @@ public void reset(Long gameId) {
* Find all users with a trainer access to a game
*
* @param id id of the game
- *
* @return
*/
public List findTrainers(Long id) {
@@ -790,7 +834,7 @@ public List findTrainers(Long id) {
* @return GameFacade instance
*/
public static GameFacade
- lookup() {
+ lookup() {
try {
return Helper.lookupBy(GameFacade.class
);
@@ -805,8 +849,6 @@ public List findTrainers(Long id) {
*
* @param gameId
* @param t
- *
- *
*/
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public Long createAndCommit(Long gameId, Team t) {
@@ -880,18 +922,18 @@ public XlsxSpreadsheet getXlsxOverview(Long gameId) {
private void loadOverviews(XlsxSpreadsheet xlsx, Game game, boolean includeTestPlayer) {
Player p = game.getTestPlayer();
String script
- = "var result= [];"
- + "if (WegasDashboard){"
- + " result = WegasDashboard.getAllOverviews(true);"
- + "}"
- + "result;";
+ = "var result= [];"
+ + "if (WegasDashboard){"
+ + " result = WegasDashboard.getAllOverviews(true);"
+ + "}"
+ + "result;";
CellStyle titleStyle = xlsx.createHeaderStyle();
titleStyle.setAlignment(HorizontalAlignment.CENTER);
CellStyle subtitleStyle = xlsx.createSmallerHeaderStyle();
- try ( ScriptExecutionContext ctx = requestManager.switchToInternalExecContext(true)) {
+ try (ScriptExecutionContext ctx = requestManager.switchToInternalExecContext(true)) {
ScriptObjectMirror overviews = (ScriptObjectMirror) scriptFacade.eval(p, new Script(script), null);
for (Object oSheet : overviews.values()) {
diff --git a/wegas-core/src/main/java/com/wegas/core/rest/GameController.java b/wegas-core/src/main/java/com/wegas/core/rest/GameController.java
index 6b4c371a3e..aedb3e0344 100644
--- a/wegas-core/src/main/java/com/wegas/core/rest/GameController.java
+++ b/wegas-core/src/main/java/com/wegas/core/rest/GameController.java
@@ -19,6 +19,8 @@
import com.wegas.core.persistence.game.Game.Status;
import com.wegas.core.persistence.game.Player;
import com.wegas.core.persistence.game.Team;
+import com.wegas.core.rest.util.pagination.Page;
+import com.wegas.core.rest.util.pagination.Pageable;
import com.wegas.core.security.ejb.UserFacade;
import com.wegas.core.security.persistence.User;
import java.io.IOException;
@@ -35,15 +37,7 @@
import jakarta.ejb.Stateless;
import jakarta.inject.Inject;
import jakarta.servlet.http.HttpServletRequest;
-import jakarta.ws.rs.Consumes;
-import jakarta.ws.rs.DELETE;
-import jakarta.ws.rs.GET;
-import jakarta.ws.rs.POST;
-import jakarta.ws.rs.PUT;
-import jakarta.ws.rs.Path;
-import jakarta.ws.rs.PathParam;
-import jakarta.ws.rs.Produces;
-import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.*;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
@@ -219,6 +213,31 @@ public Collection findByStatus(@PathParam("status") final Game.Status stat
return gameFacade.findByStatusAndUser(status);
}
+ /**
+ * Get all games with given status paginated
+ *
+ */
+ @GET
+ @Path("status/{status: [A-Z]*}/Paginated")
+ public Page paginatedGames(@PathParam("status") final Game.Status status,
+ @QueryParam("page") int page,
+ @QueryParam("size") int size,
+ @QueryParam("query") String query) {
+ return gameFacade.findByStatusAndUserPaginated(status, new Pageable(page, size, query));
+ }
+ /**
+ * Get all games with given status paginated
+ *
+ */
+ @GET
+ @Path("status/{status: [A-Z]*}/Paginated2")
+ public Page paginatedGames2(@PathParam("status") final Game.Status status,
+ @QueryParam("page") int page,
+ @QueryParam("size") int size,
+ @QueryParam("query") String query) {
+ return gameFacade.findByStatusAndUserPaginated2(status, new Pageable(page, size, query));
+ }
+
/**
* Count games by status
*
From 4060fce360995486e4ef5fa938b5128778c22e2a Mon Sep 17 00:00:00 2001
From: Mikkel Vestergaard <56039404+TehAwol@users.noreply.github.com>
Date: Mon, 29 Apr 2024 10:28:22 +0200
Subject: [PATCH 02/11] Restore old query, add paginated query params
---
.../java/com/wegas/core/ejb/GameFacade.java | 23 ++++++++++++-------
.../com/wegas/core/rest/GameController.java | 12 ----------
2 files changed, 15 insertions(+), 20 deletions(-)
diff --git a/wegas-core/src/main/java/com/wegas/core/ejb/GameFacade.java b/wegas-core/src/main/java/com/wegas/core/ejb/GameFacade.java
index b4b9ffff7f..2fd0d53ac9 100644
--- a/wegas-core/src/main/java/com/wegas/core/ejb/GameFacade.java
+++ b/wegas-core/src/main/java/com/wegas/core/ejb/GameFacade.java
@@ -434,15 +434,20 @@ public Page findByStatusAndUserPaginated(Game.Status status, Pageable page
gameRoot.get("id").in(new ArrayList<>(filteredGMatrix.keySet()))
);
-
- if (pageable.getQuery() != null) {
- String lowerCaseQuery = pageable.getQuery().toLowerCase();
- Join gameModelJoin = gameRoot.join("gameModel");
- whereClause = criteriaBuilder.and(whereClause, criteriaBuilder.or(
- criteriaBuilder.like(criteriaBuilder.lower(gameRoot.get("name")), "%" + lowerCaseQuery + "%"),
- criteriaBuilder.like(criteriaBuilder.lower(gameModelJoin.get("name")), "%" + lowerCaseQuery + "%")
- ));
+ ListIterator queryParamsIterator = pageable.getSplitQuery().listIterator();
+
+ while (queryParamsIterator.hasNext()) {
+ String param = queryParamsIterator.next();
+ ParameterExpression queryParameter = criteriaBuilder.parameter(String.class);
+ if (!param.isEmpty()) {
+ Join gameModelJoin = gameRoot.join("gameModel");
+ whereClause = criteriaBuilder.and(whereClause, criteriaBuilder.or(
+ criteriaBuilder.like(criteriaBuilder.lower(gameRoot.get("name")), "%" + param.toLowerCase() + "%"),
+ criteriaBuilder.like(criteriaBuilder.lower(gameModelJoin.get("name")), "%" + param.toLowerCase() + "%")
+ ));
+ }
}
+
query.where(whereClause);
int total = getEntityManager().createQuery(query).getResultList().size();
@@ -485,6 +490,8 @@ public Collection findByStatusAndUser(Game.Status status) {
List gStatuses = new ArrayList<>();
gStatuses.add(status);
+ Map> gMatrix = this.getPermissionMatrix(gStatuses);
+
ArrayList games = new ArrayList<>();
for (Map.Entry> entry : gMatrix.entrySet()) {
diff --git a/wegas-core/src/main/java/com/wegas/core/rest/GameController.java b/wegas-core/src/main/java/com/wegas/core/rest/GameController.java
index aedb3e0344..92adcb7579 100644
--- a/wegas-core/src/main/java/com/wegas/core/rest/GameController.java
+++ b/wegas-core/src/main/java/com/wegas/core/rest/GameController.java
@@ -225,18 +225,6 @@ public Page paginatedGames(@PathParam("status") final Game.Status status,
@QueryParam("query") String query) {
return gameFacade.findByStatusAndUserPaginated(status, new Pageable(page, size, query));
}
- /**
- * Get all games with given status paginated
- *
- */
- @GET
- @Path("status/{status: [A-Z]*}/Paginated2")
- public Page paginatedGames2(@PathParam("status") final Game.Status status,
- @QueryParam("page") int page,
- @QueryParam("size") int size,
- @QueryParam("query") String query) {
- return gameFacade.findByStatusAndUserPaginated2(status, new Pageable(page, size, query));
- }
/**
* Count games by status
From f3fc52bd08a3f568e34bcae67a8c77984afbc22d Mon Sep 17 00:00:00 2001
From: Mikkel Vestergaard <56039404+TehAwol@users.noreply.github.com>
Date: Fri, 3 May 2024 17:56:29 +0200
Subject: [PATCH 03/11] Uncomplete pagination for Dev tests
---
.github/workflows/maven.yml | 1 +
.../src/main/node/wegas-lobby/src/API/api.ts | 23 +++---
.../node/wegas-lobby/src/API/restClient.ts | 37 ++++++++--
.../src/components/trainer/TrainerTab.tsx | 71 +++++++++++++++++--
.../src/selectors/wegasSelector.ts | 2 +
.../node/wegas-lobby/src/store/slices/game.ts | 22 ++++++
.../wegas-lobby/src/store/slices/gameModel.ts | 10 +++
.../java/com/wegas/core/ejb/GameFacade.java | 8 +--
8 files changed, 146 insertions(+), 28 deletions(-)
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index eb8aa7cf55..89683e69c2 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -12,6 +12,7 @@ on:
branches:
- master
- dev
+ - WEG-11
tags:
- "v*"
diff --git a/wegas-app/src/main/node/wegas-lobby/src/API/api.ts b/wegas-app/src/main/node/wegas-lobby/src/API/api.ts
index 780561f236..8111218624 100644
--- a/wegas-app/src/main/node/wegas-lobby/src/API/api.ts
+++ b/wegas-app/src/main/node/wegas-lobby/src/API/api.ts
@@ -27,15 +27,14 @@ import { getStore, WegasLobbyState } from '../store/store';
import { CHANNEL_PREFIX, getPusherClient, initPusherSocket } from '../websocket/websocket';
import { entityIs, entityIsException } from './entityHelper';
import {
- IAccountWithPerm,
- IAuthenticationInformation,
- IGameAdminWithTeams,
- IJpaAuthentication,
- IRoleWithPermissions,
- IUserPage,
- IUserWithAccounts,
- PlayerToGameModel,
- WegasLobbyRestClient,
+ IAccountWithPerm,
+ IAuthenticationInformation,
+ IGameAdminWithTeams,
+ IJpaAuthentication, IPage,
+ IRoleWithPermissions,
+ IUserWithAccounts,
+ PlayerToGameModel,
+ WegasLobbyRestClient,
} from './restClient';
const logger = getLogger('api');
@@ -363,7 +362,7 @@ export const getAllUsers = createAsyncThunk(
export const getPaginatedUsers = createAsyncThunk(
'user/getPaginated',
- async (payload: {page: number, size: number, query: string}): Promise => {
+ async (payload: {page: number, size: number, query: string}): Promise> => {
return await restClient.UserController.getPaginatedUsers(payload.page, payload.size, payload.query);
},
);
@@ -608,6 +607,10 @@ export const getGames = createAsyncThunk('game/getGames', async (status: IGameWi
return await restClient.GameController.getGames(status);
});
+export const getGamesPaginated = createAsyncThunk('game/getGamesPaginated', async (payload: {status: IGameWithId['status'], page: number, size: number, query: string}) => {
+ return await restClient.GameController.getGamesPaginated(payload.status, payload.page, payload.size, payload.query);
+});
+
export const changeGameStatus = createAsyncThunk(
'game/changeStatus',
async ({ gameId, status }: { gameId: number; status: IGameWithId['status'] }) => {
diff --git a/wegas-app/src/main/node/wegas-lobby/src/API/restClient.ts b/wegas-app/src/main/node/wegas-lobby/src/API/restClient.ts
index af0f92b797..151c780cc3 100644
--- a/wegas-app/src/main/node/wegas-lobby/src/API/restClient.ts
+++ b/wegas-app/src/main/node/wegas-lobby/src/API/restClient.ts
@@ -35,6 +35,13 @@ export interface WegasErrorMessage {
export type WegasRuntimeException = WegasErrorMessage;
+export type IPage = {
+ total: number;
+ page: number;
+ pageSize: number;
+ pageContent: T[];
+}
+
export interface OnlineUser {
fullname: string;
email: string;
@@ -57,18 +64,19 @@ export type IRoleWithPermissions = IRoleWithId & {
permissions?: IPermissionWithId[];
};
-export type IUserPage = {
- total: number;
- page: number;
- pageSize: number;
- pageContent:IUserWithAccounts[];
-};
+// export type IUserPage = {
+// total: number;
+// page: number;
+// pageSize: number;
+// pageContent: IUserWithAccounts[];
+// };
export type IUserWithAccounts = IUserWithId & {
accounts?: IAccountWithPerm[];
permissions?: IPermissionWithId[];
roles?: IRoleWithPermissions[];
};
+
export interface IJpaAuthentication {
'@class': 'JpaAuthentication';
mandatoryMethod: HashMethod;
@@ -182,6 +190,7 @@ export interface IGameAdminTeam {
declaredSize: number;
players?: IGameAdminPlayer[];
}
+
export interface IGameAdminWithTeams extends IGameAdminWithId {
teams?: IGameAdminTeam[];
effectiveCount: number;
@@ -558,7 +567,7 @@ export const WegasLobbyRestClient = function (
},
getPaginatedUsers: (page: number, size: number, query: string) => {
const path = `${baseUrl}/Shadow/User/Paginated?page=${page}&size=${size}&query=${query}`;
- return sendJsonRequest('GET', path, undefined, errorHandler);
+ return sendJsonRequest>('GET', path, undefined, errorHandler);
},
getUser: (userId: number) => {
const path = `${baseUrl}/User/${userId}`;
@@ -726,6 +735,20 @@ export const WegasLobbyRestClient = function (
errorHandler,
);
},
+ getGamesPaginated: (
+ status: IGameWithId['status'],
+ page: number,
+ size: number,
+ query: string,
+ ) => {
+ const path = `${baseUrl}/Lobby/GameModel/Game/status/${status}/Paginated?page=${page}&size=${size}&query=${query}`;
+ return sendJsonRequest>(
+ 'GET',
+ path,
+ undefined,
+ errorHandler
+ )
+ },
changeStatus: (gameId: number, status: IGameWithId['status']) => {
const path = `${baseUrl}/Lobby/GameModel/Game/${gameId}/status/${status}`;
return sendJsonRequest('PUT', path, undefined, errorHandler);
diff --git a/wegas-app/src/main/node/wegas-lobby/src/components/trainer/TrainerTab.tsx b/wegas-app/src/main/node/wegas-lobby/src/components/trainer/TrainerTab.tsx
index f6a5b106c0..b8d2a3067a 100644
--- a/wegas-app/src/main/node/wegas-lobby/src/components/trainer/TrainerTab.tsx
+++ b/wegas-app/src/main/node/wegas-lobby/src/components/trainer/TrainerTab.tsx
@@ -10,15 +10,15 @@ import { css } from '@emotion/css';
import { faPlusCircle } from '@fortawesome/free-solid-svg-icons';
import { uniq } from 'lodash';
import * as React from 'react';
-import {useMatch, useResolvedPath} from 'react-router-dom';
+import { useMatch, useResolvedPath } from 'react-router-dom';
import { IAbstractAccount, IGameModelWithId, IGameWithId } from 'wegas-ts-api';
-import { getGames, getShadowUserByIds } from '../../API/api';
+import { getGamesPaginated, getShadowUserByIds } from '../../API/api';
import { getDisplayName, mapByKey, match } from '../../helper';
import useTranslations from '../../i18n/I18nContext';
import { useLocalStorageState } from '../../preferences';
import { useAccountsByUserIds, useCurrentUser } from '../../selectors/userSelector';
import { MINE_OR_ALL, useGames } from '../../selectors/wegasSelector';
-import { useAppDispatch } from '../../store/hooks';
+import {useAppDispatch} from '../../store/hooks';
import { WindowedContainer } from '../common/CardContainer';
import DebouncedInput from '../common/DebouncedInput';
import DropDownMenu, { itemStyle } from '../common/DropDownMenu';
@@ -32,6 +32,7 @@ import { successColor } from '../styling/color';
import { panelPadding } from '../styling/style';
import CreateGame from './CreateGame';
import GameCard from './GameCard';
+import Checkbox from "../common/Checkbox";
interface SortBy {
createdByName: string;
@@ -94,6 +95,11 @@ export default function TrainerTab(): JSX.Element {
// }, []);
const [filter, setFilter] = React.useState('');
+ const [page, setPage] = React.useState(1);
+ const [pageSize, setPageSize] = React.useState(20);
+
+ const onNextPage = () => setPage(page setPage(page>1?page - 1:1);
const onFilterChange = React.useCallback((filter: string) => {
setFilter(filter);
@@ -111,10 +117,36 @@ export default function TrainerTab(): JSX.Element {
React.useEffect(() => {
if (status === 'NOT_INITIALIZED') {
- dispatch(getGames(statusFilter));
+ dispatch(
+ getGamesPaginated({ status: statusFilter, page: page, size: pageSize, query: filter }),
+ );
+ console.log(games.totalResults)
}
}, [status, dispatch, statusFilter]);
+ React.useEffect(() => {
+ if (page !== 1) {
+ setPage(1);
+ } else {
+ dispatch(
+ getGamesPaginated({ status: statusFilter, page: page, size: pageSize, query: filter }),
+ );
+ console.log(games.totalResults)
+ }
+ }, [filter, pageSize]);
+
+ React.useEffect(() => {
+ dispatch(
+ getGamesPaginated({ status: statusFilter, page: page, size: pageSize, query: filter }),
+ );
+ }, [page]);
+
+ // React.useEffect(() => {
+ // if (status === 'NOT_INITIALIZED') {
+ // dispatch(getGames(statusFilter));
+ // }
+ // }, [status, dispatch, statusFilter]);
+
const userIds = uniq(
games.gamesAndGameModels.flatMap(data =>
data.game.createdById != null ? [data.game.createdById] : [],
@@ -142,7 +174,7 @@ export default function TrainerTab(): JSX.Element {
}, [isAdmin, accountsState, dispatch]);
// Detect any gameModel id in URL
- const resolvedPath = useResolvedPath("./");
+ const resolvedPath = useResolvedPath('./');
const match = useMatch<'id', string>(`${resolvedPath.pathname}:id/*`);
const selectedId = Number(match?.params.id) || undefined;
@@ -264,6 +296,35 @@ export default function TrainerTab(): JSX.Element {
/>
+
+
+
+
+ {page}/{games.totalResults > 0 ? Math.ceil(games.totalResults/pageSize) : 1}
+
+
+
+
+ setPageSize(newValue?20:pageSize)} />
+ setPageSize(newValue?50:pageSize)} />
+ setPageSize(newValue?100:pageSize)} />
+
+
+
{status === 'READY' ? (
<>
;
teams: Record;
joinStatus: Record;
+ totalResults: number;
}
const initialState: GameState = {
@@ -31,6 +32,7 @@ const initialState: GameState = {
games: {},
teams: {},
joinStatus: {},
+ totalResults: 0,
};
const slice = createSlice({
@@ -106,6 +108,26 @@ const slice = createSlice({
),
};
})
+ .addCase(API.getGamesPaginated.pending, (state, action) => {
+ const status = action.meta.arg.status;
+ state.status[status] = 'LOADING';
+ })
+ .addCase(API.getGamesPaginated.fulfilled, (state, action) => {
+ const status = action.meta.arg.status;
+ state.status[status] = 'READY';
+
+ state.totalResults = action.payload.total;
+
+ state.games = {
+ ...mapById(
+ action.payload.pageContent.map(game => {
+ const g = { ...game };
+ delete g.gameModel;
+ return g;
+ }),
+ ),
+ };
+ })
.addCase(API.getAllTeams.pending, (state, action) => {
state.teams[action.meta.arg] = 'LOADING';
})
diff --git a/wegas-app/src/main/node/wegas-lobby/src/store/slices/gameModel.ts b/wegas-app/src/main/node/wegas-lobby/src/store/slices/gameModel.ts
index c56d498bc0..64702b9923 100644
--- a/wegas-app/src/main/node/wegas-lobby/src/store/slices/gameModel.ts
+++ b/wegas-app/src/main/node/wegas-lobby/src/store/slices/gameModel.ts
@@ -112,6 +112,16 @@ const slice = createSlice({
}
});
})
+ .addCase(API.getGamesPaginated.fulfilled, (state, action) => {
+ state.games = [];
+ state.gameModels = [];
+ action.payload.pageContent.forEach(game => {
+ if (game.gameModel != null) {
+ state.gameModels[game.gameModel.id] = game.gameModel;
+ updateParent(state, game.gameModel.id, game.id);
+ }
+ });
+ })
.addCase(API.getPlayers.fulfilled, (state, action) => {
action.payload.forEach(data => {
if (data.gameModel != null) {
diff --git a/wegas-core/src/main/java/com/wegas/core/ejb/GameFacade.java b/wegas-core/src/main/java/com/wegas/core/ejb/GameFacade.java
index 2fd0d53ac9..8b67d0c79e 100644
--- a/wegas-core/src/main/java/com/wegas/core/ejb/GameFacade.java
+++ b/wegas-core/src/main/java/com/wegas/core/ejb/GameFacade.java
@@ -428,19 +428,15 @@ public Page findByStatusAndUserPaginated(Game.Status status, Pageable page
Root gameRoot = query.from(Game.class);
query.select(gameRoot);
-
Predicate whereClause = criteriaBuilder.and(
criteriaBuilder.equal(gameRoot.get("status"), status),
gameRoot.get("id").in(new ArrayList<>(filteredGMatrix.keySet()))
);
- ListIterator queryParamsIterator = pageable.getSplitQuery().listIterator();
-
- while (queryParamsIterator.hasNext()) {
- String param = queryParamsIterator.next();
+ for (String param : pageable.getSplitQuery()) {
ParameterExpression queryParameter = criteriaBuilder.parameter(String.class);
if (!param.isEmpty()) {
- Join gameModelJoin = gameRoot.join("gameModel");
+ Join gameModelJoin = gameRoot.join("gameModel", JoinType.INNER);
whereClause = criteriaBuilder.and(whereClause, criteriaBuilder.or(
criteriaBuilder.like(criteriaBuilder.lower(gameRoot.get("name")), "%" + param.toLowerCase() + "%"),
criteriaBuilder.like(criteriaBuilder.lower(gameModelJoin.get("name")), "%" + param.toLowerCase() + "%")
From 03f0a2e9085c9ae5afed116ddc3e28ddd9a9fc13 Mon Sep 17 00:00:00 2001
From: Mikkel Vestergaard <56039404+TehAwol@users.noreply.github.com>
Date: Fri, 24 May 2024 16:36:42 +0200
Subject: [PATCH 04/11] Add author query, clean TrainerTab
---
.../node/wegas-lobby/src/API/restClient.ts | 2 +-
.../src/components/trainer/TrainerTab.tsx | 135 +++++++-----------
.../src/main/node/wegas-lobby/src/i18n/fr.ts | 2 +-
.../java/com/wegas/core/ejb/GameFacade.java | 42 ++----
.../com/wegas/core/rest/GameController.java | 6 +-
5 files changed, 67 insertions(+), 120 deletions(-)
diff --git a/wegas-app/src/main/node/wegas-lobby/src/API/restClient.ts b/wegas-app/src/main/node/wegas-lobby/src/API/restClient.ts
index 151c780cc3..de55d304f6 100644
--- a/wegas-app/src/main/node/wegas-lobby/src/API/restClient.ts
+++ b/wegas-app/src/main/node/wegas-lobby/src/API/restClient.ts
@@ -739,7 +739,7 @@ export const WegasLobbyRestClient = function (
status: IGameWithId['status'],
page: number,
size: number,
- query: string,
+ query: string
) => {
const path = `${baseUrl}/Lobby/GameModel/Game/status/${status}/Paginated?page=${page}&size=${size}&query=${query}`;
return sendJsonRequest>(
diff --git a/wegas-app/src/main/node/wegas-lobby/src/components/trainer/TrainerTab.tsx b/wegas-app/src/main/node/wegas-lobby/src/components/trainer/TrainerTab.tsx
index b8d2a3067a..b8cdbb0139 100644
--- a/wegas-app/src/main/node/wegas-lobby/src/components/trainer/TrainerTab.tsx
+++ b/wegas-app/src/main/node/wegas-lobby/src/components/trainer/TrainerTab.tsx
@@ -11,14 +11,13 @@ import { faPlusCircle } from '@fortawesome/free-solid-svg-icons';
import { uniq } from 'lodash';
import * as React from 'react';
import { useMatch, useResolvedPath } from 'react-router-dom';
-import { IAbstractAccount, IGameModelWithId, IGameWithId } from 'wegas-ts-api';
+import { IGameModelWithId, IGameWithId } from 'wegas-ts-api';
import { getGamesPaginated, getShadowUserByIds } from '../../API/api';
-import { getDisplayName, mapByKey, match } from '../../helper';
import useTranslations from '../../i18n/I18nContext';
import { useLocalStorageState } from '../../preferences';
import { useAccountsByUserIds, useCurrentUser } from '../../selectors/userSelector';
import { MINE_OR_ALL, useGames } from '../../selectors/wegasSelector';
-import {useAppDispatch} from '../../store/hooks';
+import { useAppDispatch } from '../../store/hooks';
import { WindowedContainer } from '../common/CardContainer';
import DebouncedInput from '../common/DebouncedInput';
import DropDownMenu, { itemStyle } from '../common/DropDownMenu';
@@ -27,32 +26,11 @@ import FitSpace from '../common/FitSpace';
import Flex from '../common/Flex';
import IconButton from '../common/IconButton';
import InlineLoading from '../common/InlineLoading';
-import SortBy, { SortByOption } from '../common/SortBy';
import { successColor } from '../styling/color';
import { panelPadding } from '../styling/style';
import CreateGame from './CreateGame';
import GameCard from './GameCard';
-import Checkbox from "../common/Checkbox";
-
-interface SortBy {
- createdByName: string;
- name: string;
- createdTime: number;
-}
-
-const matchSearch =
- (accountMap: Record, search: string) =>
- ({ game, gameModel }: { game: IGameWithId; gameModel: IGameModelWithId }) => {
- return match(search, regex => {
- const username = game.createdById != null ? getDisplayName(accountMap[game.createdById]) : '';
- return (
- (gameModel.name && gameModel.name.match(regex) != null) ||
- (game.name && game.name.match(regex) != null) ||
- (game.token && game.token.match(regex) != null) ||
- username.match(regex) != null
- );
- });
- };
+import Checkbox from '../common/Checkbox';
export default function TrainerTab(): JSX.Element {
const i18n = useTranslations();
@@ -82,14 +60,6 @@ export default function TrainerTab(): JSX.Element {
const [viewMode, setViewMode] = React.useState<'EXPANDED' | 'COLLAPSED'>('COLLAPSED');
- const [sortBy, setSortBy] = useLocalStorageState<{ key: keyof SortBy; asc: boolean }>(
- 'trainer-sortby',
- {
- key: 'createdTime',
- asc: false,
- },
- );
-
// const onSortChange = React.useCallback(({ key, asc }: { key: keyof SortBy; asc: boolean }) => {
// setSortBy({ key, asc });
// }, []);
@@ -98,21 +68,13 @@ export default function TrainerTab(): JSX.Element {
const [page, setPage] = React.useState(1);
const [pageSize, setPageSize] = React.useState(20);
- const onNextPage = () => setPage(page setPage(page>1?page - 1:1);
+ const onNextPage = () => setPage(page < games.totalResults / pageSize ? page + 1 : page);
+ const onPreviousPage = () => setPage(page > 1 ? page - 1 : 1);
const onFilterChange = React.useCallback((filter: string) => {
setFilter(filter);
}, []);
- const sortOptions: SortByOption[] = [
- { key: 'createdTime', label: i18n.date },
- { key: 'name', label: i18n.name },
- ];
- if (isAdmin) {
- sortOptions.push({ key: 'createdByName', label: i18n.createdBy });
- }
-
const status = games.status[statusFilter];
React.useEffect(() => {
@@ -120,7 +82,6 @@ export default function TrainerTab(): JSX.Element {
dispatch(
getGamesPaginated({ status: statusFilter, page: page, size: pageSize, query: filter }),
);
- console.log(games.totalResults)
}
}, [status, dispatch, statusFilter]);
@@ -131,13 +92,12 @@ export default function TrainerTab(): JSX.Element {
dispatch(
getGamesPaginated({ status: statusFilter, page: page, size: pageSize, query: filter }),
);
- console.log(games.totalResults)
}
}, [filter, pageSize]);
React.useEffect(() => {
dispatch(
- getGamesPaginated({ status: statusFilter, page: page, size: pageSize, query: filter }),
+ getGamesPaginated({ status: statusFilter, page: page, size: pageSize, query: filter }),
);
}, [page]);
@@ -165,7 +125,6 @@ export default function TrainerTab(): JSX.Element {
);
const accountsState = useAccountsByUserIds(userIds);
- const accounts = mapByKey(accountsState.accounts, 'parentId');
React.useEffect(() => {
if (isAdmin && accountsState.unknownUsers.length > 0) {
@@ -184,24 +143,6 @@ export default function TrainerTab(): JSX.Element {
} else {
const selected = games.gamesAndGameModels.find(ggm => ggm.gameModel.id === selectedId);
- const filtered = filter
- ? games.gamesAndGameModels.filter(matchSearch(accounts, filter))
- : games.gamesAndGameModels;
- const sorted = filtered.sort((a, b) => {
- const reverse = sortBy.asc ? 1 : -1;
- if (sortBy.key === 'createdTime') {
- return reverse * (a.game.createdTime! - b.game.createdTime!);
- } else if (sortBy.key === 'name') {
- const aName =
- a.game.createdById != null ? getDisplayName(accounts[a.game.createdById]) : '';
- const bName =
- b.game.createdById != null ? getDisplayName(accounts[b.game.createdById]) : '';
-
- return reverse * aName.localeCompare(bName);
- }
- return 0;
- });
-
const statusFilterEntries: { value: IGameWithId['status']; label: React.ReactNode }[] = [
{
value: 'LIVE',
@@ -283,7 +224,6 @@ export default function TrainerTab(): JSX.Element {
>
{i18n.createGame}
-
{dropDownStatus}
{dropDownMine}
@@ -297,38 +237,61 @@ export default function TrainerTab(): JSX.Element {
+
-
-
+ >
+ {`${games.totalResults} ${i18n.games}`}
+
+
+
- {page}/{games.totalResults > 0 ? Math.ceil(games.totalResults/pageSize) : 1}
+ {page}/{games.totalResults > 0 ? Math.ceil(games.totalResults / pageSize) : 1}
-
-
setPageSize(newValue?20:pageSize)} />
- setPageSize(newValue?50:pageSize)} />
- setPageSize(newValue?100:pageSize)} />
+
+ setPageSize(newValue ? 20 : pageSize)}
+ />
+ setPageSize(newValue ? 50 : pageSize)}
+ />
+ setPageSize(newValue ? 100 : pageSize)}
+ />
{status === 'READY' ? (
<>
{filter ? i18n.noGamesFound : i18n.noGames}}
>
diff --git a/wegas-app/src/main/node/wegas-lobby/src/i18n/fr.ts b/wegas-app/src/main/node/wegas-lobby/src/i18n/fr.ts
index 4f061aa4f1..0512a1fc36 100644
--- a/wegas-app/src/main/node/wegas-lobby/src/i18n/fr.ts
+++ b/wegas-app/src/main/node/wegas-lobby/src/i18n/fr.ts
@@ -95,7 +95,7 @@ export const fr: WegasTranslations = {
gameNotFound: "clé d'accès invalide",
//
Game: 'Partie',
- games: 'parites',
+ games: 'parties',
allGames: 'Toutes les parties',
archive: 'achiver',
restore: 'restaurer',
diff --git a/wegas-core/src/main/java/com/wegas/core/ejb/GameFacade.java b/wegas-core/src/main/java/com/wegas/core/ejb/GameFacade.java
index 8b67d0c79e..5e1046dfed 100644
--- a/wegas-core/src/main/java/com/wegas/core/ejb/GameFacade.java
+++ b/wegas-core/src/main/java/com/wegas/core/ejb/GameFacade.java
@@ -382,30 +382,6 @@ public List findAll(final Game.Status status) {
.setParameter("status", status).getResultList();
}
-// /**
-// * Find all games with given ids and status (was used for faster findByStatusAndUser fetch)
-// *
-// * @param ids ids of games to fetch
-// * @param status status of games to fetch
-// * @return all games which match given ids and status
-// */
-// public List findByIdsAndStatus(final List ids, final Game.Status status) {
-// final CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder();
-// final CriteriaQuery query = criteriaBuilder.createQuery(Game.class);
-// Root gameRoot = query.from(Game.class);
-//
-// query.where(
-// criteriaBuilder.and(
-// gameRoot.get("id").in(ids),
-// criteriaBuilder.equal(gameRoot.get("status"), status)
-// )
-// );
-//
-// TypedQuery typedQuery = getEntityManager().createQuery(query);
-//
-// return typedQuery.getResultList();
-// }
-
/**
* Get all paginated games with the given status which are accessible to the current user
*
@@ -430,21 +406,29 @@ public Page findByStatusAndUserPaginated(Game.Status status, Pageable page
Predicate whereClause = criteriaBuilder.and(
criteriaBuilder.equal(gameRoot.get("status"), status),
+ // Maximum in values for psql: 32767
gameRoot.get("id").in(new ArrayList<>(filteredGMatrix.keySet()))
);
for (String param : pageable.getSplitQuery()) {
ParameterExpression queryParameter = criteriaBuilder.parameter(String.class);
if (!param.isEmpty()) {
- Join gameModelJoin = gameRoot.join("gameModel", JoinType.INNER);
+ Join gameModelJoin = gameRoot.join("gameModel", JoinType.INNER);
+
+ // Only if admin
+ Join userJoin = gameRoot.join("createdBy", JoinType.INNER);
+ Join abstractAccountJoin = userJoin.join("accounts", JoinType.INNER);
+ Expression exp = criteriaBuilder.concat(criteriaBuilder.lower(criteriaBuilder.coalesce(abstractAccountJoin.get("firstname"), "")), " ");
+ exp = criteriaBuilder.concat(exp, criteriaBuilder.lower(criteriaBuilder.coalesce(abstractAccountJoin.get("lastname"), "")));
+
whereClause = criteriaBuilder.and(whereClause, criteriaBuilder.or(
- criteriaBuilder.like(criteriaBuilder.lower(gameRoot.get("name")), "%" + param.toLowerCase() + "%"),
- criteriaBuilder.like(criteriaBuilder.lower(gameModelJoin.get("name")), "%" + param.toLowerCase() + "%")
- ));
+ criteriaBuilder.like(criteriaBuilder.lower(gameRoot.get("name")), "%" + param.toLowerCase() + "%"),
+ criteriaBuilder.like(criteriaBuilder.lower(gameModelJoin.get("name")), "%" + param.toLowerCase() + "%"),
+ criteriaBuilder.like(criteriaBuilder.lower(exp), "%" + param.toLowerCase() + "%")));
}
}
-
query.where(whereClause);
+ query.orderBy(criteriaBuilder.desc(gameRoot.get("createdTime")));
int total = getEntityManager().createQuery(query).getResultList().size();
TypedQuery listQuery = pageable.paginateQuery(getEntityManager().createQuery(query));
diff --git a/wegas-core/src/main/java/com/wegas/core/rest/GameController.java b/wegas-core/src/main/java/com/wegas/core/rest/GameController.java
index 92adcb7579..c75864978a 100644
--- a/wegas-core/src/main/java/com/wegas/core/rest/GameController.java
+++ b/wegas-core/src/main/java/com/wegas/core/rest/GameController.java
@@ -220,9 +220,9 @@ public Collection findByStatus(@PathParam("status") final Game.Status stat
@GET
@Path("status/{status: [A-Z]*}/Paginated")
public Page paginatedGames(@PathParam("status") final Game.Status status,
- @QueryParam("page") int page,
- @QueryParam("size") int size,
- @QueryParam("query") String query) {
+ @QueryParam("page") int page,
+ @QueryParam("size") int size,
+ @QueryParam("query") String query) {
return gameFacade.findByStatusAndUserPaginated(status, new Pageable(page, size, query));
}
From 4d57dca831cfedcf9ba778b4431b9e0e1adb4dfc Mon Sep 17 00:00:00 2001
From: Mikkel Vestergaard <56039404+TehAwol@users.noreply.github.com>
Date: Fri, 24 May 2024 16:37:30 +0200
Subject: [PATCH 05/11] Remove branch from workflow
---
.github/workflows/maven.yml | 1 -
1 file changed, 1 deletion(-)
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 89683e69c2..eb8aa7cf55 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -12,7 +12,6 @@ on:
branches:
- master
- dev
- - WEG-11
tags:
- "v*"
From 8457c86bb97976fb4bab8aab4f53f5664837287b Mon Sep 17 00:00:00 2001
From: Mikkel Vestergaard <56039404+TehAwol@users.noreply.github.com>
Date: Mon, 27 May 2024 11:01:22 +0200
Subject: [PATCH 06/11] add statusFilter to useEffect
---
.../src/components/trainer/TrainerTab.tsx | 13 ++-----------
1 file changed, 2 insertions(+), 11 deletions(-)
diff --git a/wegas-app/src/main/node/wegas-lobby/src/components/trainer/TrainerTab.tsx b/wegas-app/src/main/node/wegas-lobby/src/components/trainer/TrainerTab.tsx
index b8cdbb0139..69ef64e6fd 100644
--- a/wegas-app/src/main/node/wegas-lobby/src/components/trainer/TrainerTab.tsx
+++ b/wegas-app/src/main/node/wegas-lobby/src/components/trainer/TrainerTab.tsx
@@ -93,7 +93,7 @@ export default function TrainerTab(): JSX.Element {
getGamesPaginated({ status: statusFilter, page: page, size: pageSize, query: filter }),
);
}
- }, [filter, pageSize]);
+ }, [filter, pageSize, statusFilter]);
React.useEffect(() => {
dispatch(
@@ -101,11 +101,6 @@ export default function TrainerTab(): JSX.Element {
);
}, [page]);
- // React.useEffect(() => {
- // if (status === 'NOT_INITIALIZED') {
- // dispatch(getGames(statusFilter));
- // }
- // }, [status, dispatch, statusFilter]);
const userIds = uniq(
games.gamesAndGameModels.flatMap(data =>
@@ -253,11 +248,7 @@ export default function TrainerTab(): JSX.Element {
{`${games.totalResults} ${i18n.games}`}
-
+
{page}/{games.totalResults > 0 ? Math.ceil(games.totalResults / pageSize) : 1}
From 58c9bd174e789b6ab5b98dd8dff6e37bd36d5640 Mon Sep 17 00:00:00 2001
From: Mikkel Vestergaard <56039404+TehAwol@users.noreply.github.com>
Date: Sun, 9 Jun 2024 14:43:24 +0200
Subject: [PATCH 07/11] Fix mine/all count
---
.../src/components/trainer/TrainerTab.tsx | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/wegas-app/src/main/node/wegas-lobby/src/components/trainer/TrainerTab.tsx b/wegas-app/src/main/node/wegas-lobby/src/components/trainer/TrainerTab.tsx
index 69ef64e6fd..5aa7264bf4 100644
--- a/wegas-app/src/main/node/wegas-lobby/src/components/trainer/TrainerTab.tsx
+++ b/wegas-app/src/main/node/wegas-lobby/src/components/trainer/TrainerTab.tsx
@@ -67,8 +67,9 @@ export default function TrainerTab(): JSX.Element {
const [filter, setFilter] = React.useState('');
const [page, setPage] = React.useState(1);
const [pageSize, setPageSize] = React.useState(20);
+ const [totalResults, settotalResults] = React.useState(0);
- const onNextPage = () => setPage(page < games.totalResults / pageSize ? page + 1 : page);
+ const onNextPage = () => setPage(page < totalResults / pageSize ? page + 1 : page);
const onPreviousPage = () => setPage(page > 1 ? page - 1 : 1);
const onFilterChange = React.useCallback((filter: string) => {
@@ -83,6 +84,7 @@ export default function TrainerTab(): JSX.Element {
getGamesPaginated({ status: statusFilter, page: page, size: pageSize, query: filter }),
);
}
+ settotalResults(mineFilter === 'MINE' ? games.gamesAndGameModels.length : games.totalResults);
}, [status, dispatch, statusFilter]);
React.useEffect(() => {
@@ -93,7 +95,8 @@ export default function TrainerTab(): JSX.Element {
getGamesPaginated({ status: statusFilter, page: page, size: pageSize, query: filter }),
);
}
- }, [filter, pageSize, statusFilter]);
+ settotalResults(mineFilter === 'MINE' ? games.gamesAndGameModels.length : games.totalResults);
+ }, [filter, pageSize, statusFilter, mineFilter]);
React.useEffect(() => {
dispatch(
@@ -101,7 +104,6 @@ export default function TrainerTab(): JSX.Element {
);
}, [page]);
-
const userIds = uniq(
games.gamesAndGameModels.flatMap(data =>
data.game.createdById != null ? [data.game.createdById] : [],
@@ -245,12 +247,12 @@ export default function TrainerTab(): JSX.Element {
alignContent: 'flex-start',
})}
>
- {`${games.totalResults} ${i18n.games}`}
+ {`${totalResults} ${i18n.games}`}
- {page}/{games.totalResults > 0 ? Math.ceil(games.totalResults / pageSize) : 1}
+ {page}/{games.totalResults > 0 ? Math.ceil(totalResults / pageSize) : 1}
From 8c4694d95f65018a40d90f6dffe5f38425dfe6e4 Mon Sep 17 00:00:00 2001
From: Mikkel Vestergaard <56039404+TehAwol@users.noreply.github.com>
Date: Sun, 9 Jun 2024 14:46:02 +0200
Subject: [PATCH 08/11] Small cleanup
---
wegas-app/src/main/node/wegas-lobby/src/API/restClient.ts | 7 -------
1 file changed, 7 deletions(-)
diff --git a/wegas-app/src/main/node/wegas-lobby/src/API/restClient.ts b/wegas-app/src/main/node/wegas-lobby/src/API/restClient.ts
index de55d304f6..59476fef77 100644
--- a/wegas-app/src/main/node/wegas-lobby/src/API/restClient.ts
+++ b/wegas-app/src/main/node/wegas-lobby/src/API/restClient.ts
@@ -64,13 +64,6 @@ export type IRoleWithPermissions = IRoleWithId & {
permissions?: IPermissionWithId[];
};
-// export type IUserPage = {
-// total: number;
-// page: number;
-// pageSize: number;
-// pageContent: IUserWithAccounts[];
-// };
-
export type IUserWithAccounts = IUserWithId & {
accounts?: IAccountWithPerm[];
permissions?: IPermissionWithId[];
From d1f7c80382b0f35d6ec73d9f360af5fa8c7baf96 Mon Sep 17 00:00:00 2001
From: Mikkel Vestergaard <56039404+TehAwol@users.noreply.github.com>
Date: Mon, 10 Jun 2024 10:53:51 +0200
Subject: [PATCH 09/11] Add pagination tests
---
.../com/wegas/core/ejb/GameFacadeTest.java | 54 +++++++++++++++++++
1 file changed, 54 insertions(+)
diff --git a/wegas-core/src/test/java/com/wegas/core/ejb/GameFacadeTest.java b/wegas-core/src/test/java/com/wegas/core/ejb/GameFacadeTest.java
index 26a5e831cd..c95293d6ac 100644
--- a/wegas-core/src/test/java/com/wegas/core/ejb/GameFacadeTest.java
+++ b/wegas-core/src/test/java/com/wegas/core/ejb/GameFacadeTest.java
@@ -14,10 +14,14 @@
import com.wegas.core.persistence.variable.primitive.BooleanInstance;
import com.wegas.core.rest.GameController;
import com.wegas.core.rest.TeamController;
+import com.wegas.core.rest.util.pagination.Page;
+import com.wegas.core.rest.util.pagination.Pageable;
import com.wegas.test.arquillian.AbstractArquillianTest;
import jakarta.inject.Inject;
import org.junit.Assert;
import static org.junit.Assert.*;
+
+import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -30,6 +34,14 @@ public class GameFacadeTest extends AbstractArquillianTest {
private static final Logger logger = LoggerFactory.getLogger(GameFacadeTest.class);
+ private Game game1;
+
+ private Game game2;
+
+ private static final String GAME_1 = "Game_1";
+
+ private static final String GAME_2 = "Game_2";
+
@Inject
private GameController gameController;
@@ -299,4 +311,46 @@ public void leavingTeamNotPossible() throws CloneNotSupportedException {
created = teamFacade.find(created.getId());
Assert.assertEquals(1, created.getPlayers().size());
}
+
+ @Before
+ public void setupPaginated() throws Exception {
+ login(admin);
+
+ game1 = new Game(GAME_1);
+ game1.setGameModel(scenario);
+ game2 = new Game(GAME_2);
+ game2.setGameModel(scenario);
+
+ gameFacade.create(game1);
+ gameFacade.create(game2);
+
+ game1.setStatus(Game.Status.LIVE);
+ game2.setStatus(Game.Status.LIVE);
+
+ requestManager.clearEntities();
+ }
+
+ @Test
+ public void testFindAllGamesPaginated() {
+ Page
paginatedGames = gameFacade.findByStatusAndUserPaginated(Game.Status.LIVE ,new Pageable(1, 10, ""));
+ // We expect 3 as the superclass creates one too
+ Assert.assertEquals(3L, paginatedGames.getTotal());
+ Assert.assertTrue(paginatedGames.getPageContent().contains(game1));
+ Assert.assertTrue(paginatedGames.getPageContent().contains(game2));
+ }
+
+ @Test
+ public void testFindAllGamesPaginatedFiltered() {
+ Page paginatedGames = gameFacade.findByStatusAndUserPaginated(Game.Status.LIVE, new Pageable(1, 10, GAME_1));
+ Assert.assertEquals(1L, paginatedGames.getTotal());
+ Assert.assertTrue(paginatedGames.getPageContent().contains(game1));
+ }
+
+ @Test
+ public void testFindAllGamesPaginatedNone() {
+ Page paginatedGames = gameFacade.findByStatusAndUserPaginated(Game.Status.LIVE, new Pageable(1, 10, "æøå"));
+
+ Assert.assertEquals(0L, paginatedGames.getTotal());
+ }
+
}
From f774b9ba473ba4f25b80aaf5d642acd8a99c5aaf Mon Sep 17 00:00:00 2001
From: Mikkel Vestergaard <56039404+TehAwol@users.noreply.github.com>
Date: Wed, 19 Jun 2024 15:21:36 +0200
Subject: [PATCH 10/11] Fix client side sortBy and add callback on session
creation
---
.../src/components/trainer/CreateGame.tsx | 4 +++-
.../src/components/trainer/TrainerTab.tsx | 12 +++++++++++-
.../node/wegas-lobby/src/selectors/wegasSelector.ts | 2 +-
3 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/wegas-app/src/main/node/wegas-lobby/src/components/trainer/CreateGame.tsx b/wegas-app/src/main/node/wegas-lobby/src/components/trainer/CreateGame.tsx
index 63c9647564..3db8101cb4 100644
--- a/wegas-app/src/main/node/wegas-lobby/src/components/trainer/CreateGame.tsx
+++ b/wegas-app/src/main/node/wegas-lobby/src/components/trainer/CreateGame.tsx
@@ -25,9 +25,10 @@ import { defaultSelectStyles, mainButtonStyle } from '../styling/style';
interface CreateGameProps {
close: () => void;
+ callback: () => void;
}
-export default function CreateGame({ close }: CreateGameProps): JSX.Element {
+export default function CreateGame({ close, callback }: CreateGameProps): JSX.Element {
const dispatch = useAppDispatch();
const i18n = useTranslations();
const { currentUser } = useCurrentUser();
@@ -48,6 +49,7 @@ export default function CreateGame({ close }: CreateGameProps): JSX.Element {
if (a.meta.requestStatus === 'fulfilled') {
close();
}
+ callback();
});
}
}, [dispatch, gameModelId, name, close]);
diff --git a/wegas-app/src/main/node/wegas-lobby/src/components/trainer/TrainerTab.tsx b/wegas-app/src/main/node/wegas-lobby/src/components/trainer/TrainerTab.tsx
index 5aa7264bf4..5c56668cdd 100644
--- a/wegas-app/src/main/node/wegas-lobby/src/components/trainer/TrainerTab.tsx
+++ b/wegas-app/src/main/node/wegas-lobby/src/components/trainer/TrainerTab.tsx
@@ -53,7 +53,7 @@ export default function TrainerTab(): JSX.Element {
}, [isAdmin, statusFilter, setStatusFilter]);
const games = useGames(
- !isAdmin && statusFilter === 'DELETE' ? 'BIN' : statusFilter, //non-admin should never sees deleteds
+ !isAdmin && statusFilter === 'DELETE' ? 'BIN' : statusFilter, //non-admin should never see deleted
currentUser != null ? currentUser.id : undefined,
isAdmin ? mineFilter : 'MINE', // non-admin only see theirs
);
@@ -199,6 +199,16 @@ export default function TrainerTab(): JSX.Element {
close={() => {
setViewMode('COLLAPSED');
}}
+ callback={() =>
+ dispatch(
+ getGamesPaginated({
+ status: statusFilter,
+ page: page,
+ size: pageSize,
+ query: filter,
+ }),
+ )
+ }
/>
) : null}
diff --git a/wegas-app/src/main/node/wegas-lobby/src/selectors/wegasSelector.ts b/wegas-app/src/main/node/wegas-lobby/src/selectors/wegasSelector.ts
index 1da6182b7d..a12dcdaa83 100644
--- a/wegas-app/src/main/node/wegas-lobby/src/selectors/wegasSelector.ts
+++ b/wegas-app/src/main/node/wegas-lobby/src/selectors/wegasSelector.ts
@@ -434,7 +434,7 @@ export const useGames = (
}
}
return [];
- });
+ }).sort((a, b) => b.game.createdTime - a.game.createdTime);
return {
gamesAndGameModels: gamesAndGameModels,
From 5349ad9a4353df7985d871b942fa4dc0b10ba45f Mon Sep 17 00:00:00 2001
From: Mikkel Vestergaard <56039404+TehAwol@users.noreply.github.com>
Date: Thu, 20 Jun 2024 15:50:38 +0200
Subject: [PATCH 11/11] Add server side mine filter for admins
---
.../src/main/node/wegas-lobby/src/API/api.ts | 4 ++--
.../node/wegas-lobby/src/API/restClient.ts | 5 +++--
.../src/components/trainer/TrainerTab.tsx | 20 +++++++------------
.../java/com/wegas/core/ejb/GameFacade.java | 20 ++++++++++++++-----
.../com/wegas/core/rest/GameController.java | 5 +++--
.../com/wegas/core/ejb/GameFacadeTest.java | 15 +++++++++++---
6 files changed, 42 insertions(+), 27 deletions(-)
diff --git a/wegas-app/src/main/node/wegas-lobby/src/API/api.ts b/wegas-app/src/main/node/wegas-lobby/src/API/api.ts
index 8111218624..e76a8e5210 100644
--- a/wegas-app/src/main/node/wegas-lobby/src/API/api.ts
+++ b/wegas-app/src/main/node/wegas-lobby/src/API/api.ts
@@ -607,8 +607,8 @@ export const getGames = createAsyncThunk('game/getGames', async (status: IGameWi
return await restClient.GameController.getGames(status);
});
-export const getGamesPaginated = createAsyncThunk('game/getGamesPaginated', async (payload: {status: IGameWithId['status'], page: number, size: number, query: string}) => {
- return await restClient.GameController.getGamesPaginated(payload.status, payload.page, payload.size, payload.query);
+export const getGamesPaginated = createAsyncThunk('game/getGamesPaginated', async (payload: {status: IGameWithId['status'], page: number, size: number, query: string, mine: boolean}) => {
+ return await restClient.GameController.getGamesPaginated(payload.status, payload.page, payload.size, payload.query, payload.mine);
});
export const changeGameStatus = createAsyncThunk(
diff --git a/wegas-app/src/main/node/wegas-lobby/src/API/restClient.ts b/wegas-app/src/main/node/wegas-lobby/src/API/restClient.ts
index 59476fef77..d1d2a0540a 100644
--- a/wegas-app/src/main/node/wegas-lobby/src/API/restClient.ts
+++ b/wegas-app/src/main/node/wegas-lobby/src/API/restClient.ts
@@ -732,9 +732,10 @@ export const WegasLobbyRestClient = function (
status: IGameWithId['status'],
page: number,
size: number,
- query: string
+ query: string,
+ mine: boolean,
) => {
- const path = `${baseUrl}/Lobby/GameModel/Game/status/${status}/Paginated?page=${page}&size=${size}&query=${query}`;
+ const path = `${baseUrl}/Lobby/GameModel/Game/status/${status}/Paginated?page=${page}&size=${size}&query=${query}&mine=${mine}`;
return sendJsonRequest>(
'GET',
path,
diff --git a/wegas-app/src/main/node/wegas-lobby/src/components/trainer/TrainerTab.tsx b/wegas-app/src/main/node/wegas-lobby/src/components/trainer/TrainerTab.tsx
index 5c56668cdd..8a12cebba8 100644
--- a/wegas-app/src/main/node/wegas-lobby/src/components/trainer/TrainerTab.tsx
+++ b/wegas-app/src/main/node/wegas-lobby/src/components/trainer/TrainerTab.tsx
@@ -60,16 +60,11 @@ export default function TrainerTab(): JSX.Element {
const [viewMode, setViewMode] = React.useState<'EXPANDED' | 'COLLAPSED'>('COLLAPSED');
- // const onSortChange = React.useCallback(({ key, asc }: { key: keyof SortBy; asc: boolean }) => {
- // setSortBy({ key, asc });
- // }, []);
-
const [filter, setFilter] = React.useState('');
const [page, setPage] = React.useState(1);
const [pageSize, setPageSize] = React.useState(20);
- const [totalResults, settotalResults] = React.useState(0);
- const onNextPage = () => setPage(page < totalResults / pageSize ? page + 1 : page);
+ const onNextPage = () => setPage(page < games.totalResults / pageSize ? page + 1 : page);
const onPreviousPage = () => setPage(page > 1 ? page - 1 : 1);
const onFilterChange = React.useCallback((filter: string) => {
@@ -81,10 +76,9 @@ export default function TrainerTab(): JSX.Element {
React.useEffect(() => {
if (status === 'NOT_INITIALIZED') {
dispatch(
- getGamesPaginated({ status: statusFilter, page: page, size: pageSize, query: filter }),
+ getGamesPaginated({ status: statusFilter, page: page, size: pageSize, query: filter, mine: mineFilter === 'MINE' }),
);
}
- settotalResults(mineFilter === 'MINE' ? games.gamesAndGameModels.length : games.totalResults);
}, [status, dispatch, statusFilter]);
React.useEffect(() => {
@@ -92,15 +86,14 @@ export default function TrainerTab(): JSX.Element {
setPage(1);
} else {
dispatch(
- getGamesPaginated({ status: statusFilter, page: page, size: pageSize, query: filter }),
+ getGamesPaginated({ status: statusFilter, page: page, size: pageSize, query: filter, mine: mineFilter === 'MINE' }),
);
}
- settotalResults(mineFilter === 'MINE' ? games.gamesAndGameModels.length : games.totalResults);
}, [filter, pageSize, statusFilter, mineFilter]);
React.useEffect(() => {
dispatch(
- getGamesPaginated({ status: statusFilter, page: page, size: pageSize, query: filter }),
+ getGamesPaginated({ status: statusFilter, page: page, size: pageSize, query: filter, mine: mineFilter === 'MINE' }),
);
}, [page]);
@@ -206,6 +199,7 @@ export default function TrainerTab(): JSX.Element {
page: page,
size: pageSize,
query: filter,
+ mine: mineFilter === 'MINE',
}),
)
}
@@ -257,12 +251,12 @@ export default function TrainerTab(): JSX.Element {
alignContent: 'flex-start',
})}
>
- {`${totalResults} ${i18n.games}`}
+ {`${games.totalResults} ${i18n.games}`}
- {page}/{games.totalResults > 0 ? Math.ceil(totalResults / pageSize) : 1}
+ {page}/{games.totalResults > 0 ? Math.ceil(games.totalResults / pageSize) : 1}
diff --git a/wegas-core/src/main/java/com/wegas/core/ejb/GameFacade.java b/wegas-core/src/main/java/com/wegas/core/ejb/GameFacade.java
index 5e1046dfed..aa0cda8635 100644
--- a/wegas-core/src/main/java/com/wegas/core/ejb/GameFacade.java
+++ b/wegas-core/src/main/java/com/wegas/core/ejb/GameFacade.java
@@ -300,7 +300,7 @@ public void remove(final Game entity) {
// This is for retrocompatibility w/ game models that do not habe DebugGame
if (entity.getGameModel().getGames().size() <= 1
- && !(entity.getGameModel().getGames().get(0) instanceof DebugGame)) {// This is for retrocompatibility w/ game models that do not habe DebugGame
+ && !(entity.getGameModel().getGames().get(0) instanceof DebugGame)) {
gameModelFacade.remove(entity.getGameModel());
} else {
getEntityManager().remove(entity);
@@ -386,10 +386,11 @@ public List findAll(final Game.Status status) {
* Get all paginated games with the given status which are accessible to the current user
*
* @param status status {@link Game.Status#LIVE} {@link Game.Status#BIN} {@link Game.Status#DELETE}
+ * @param mine boolean return currentUser's or all games (admin only)
* @param pageable
* @return all games paginated
*/
- public Page findByStatusAndUserPaginated(Game.Status status, Pageable pageable) {
+ public Page findByStatusAndUserPaginated(Game.Status status, boolean mine, Pageable pageable) {
List gStatuses = new ArrayList<>();
gStatuses.add(status);
@@ -410,23 +411,32 @@ public Page findByStatusAndUserPaginated(Game.Status status, Pageable page
gameRoot.get("id").in(new ArrayList<>(filteredGMatrix.keySet()))
);
+
for (String param : pageable.getSplitQuery()) {
ParameterExpression queryParameter = criteriaBuilder.parameter(String.class);
if (!param.isEmpty()) {
Join gameModelJoin = gameRoot.join("gameModel", JoinType.INNER);
- // Only if admin
Join userJoin = gameRoot.join("createdBy", JoinType.INNER);
Join abstractAccountJoin = userJoin.join("accounts", JoinType.INNER);
Expression exp = criteriaBuilder.concat(criteriaBuilder.lower(criteriaBuilder.coalesce(abstractAccountJoin.get("firstname"), "")), " ");
exp = criteriaBuilder.concat(exp, criteriaBuilder.lower(criteriaBuilder.coalesce(abstractAccountJoin.get("lastname"), "")));
whereClause = criteriaBuilder.and(whereClause, criteriaBuilder.or(
- criteriaBuilder.like(criteriaBuilder.lower(gameRoot.get("name")), "%" + param.toLowerCase() + "%"),
- criteriaBuilder.like(criteriaBuilder.lower(gameModelJoin.get("name")), "%" + param.toLowerCase() + "%"),
+ criteriaBuilder.like(criteriaBuilder.lower(gameRoot.get("name")), "%" + param.toLowerCase() + "%"),
+ criteriaBuilder.like(criteriaBuilder.lower(gameModelJoin.get("name")), "%" + param.toLowerCase() + "%"),
criteriaBuilder.like(criteriaBuilder.lower(exp), "%" + param.toLowerCase() + "%")));
}
}
+
+ // By default admins retrieve all, mine filter is reserved for them
+ if (mine && requestManager.isAdmin()) {
+ User user = userFacade.getCurrentUser();
+ whereClause = criteriaBuilder.and(whereClause, criteriaBuilder.and(
+ criteriaBuilder.equal(gameRoot.get("createdBy"), user)
+ ));
+ }
+
query.where(whereClause);
query.orderBy(criteriaBuilder.desc(gameRoot.get("createdTime")));
diff --git a/wegas-core/src/main/java/com/wegas/core/rest/GameController.java b/wegas-core/src/main/java/com/wegas/core/rest/GameController.java
index c75864978a..f520383096 100644
--- a/wegas-core/src/main/java/com/wegas/core/rest/GameController.java
+++ b/wegas-core/src/main/java/com/wegas/core/rest/GameController.java
@@ -222,8 +222,9 @@ public Collection findByStatus(@PathParam("status") final Game.Status stat
public Page paginatedGames(@PathParam("status") final Game.Status status,
@QueryParam("page") int page,
@QueryParam("size") int size,
- @QueryParam("query") String query) {
- return gameFacade.findByStatusAndUserPaginated(status, new Pageable(page, size, query));
+ @QueryParam("query") String query,
+ @QueryParam("mine") boolean mine) {
+ return gameFacade.findByStatusAndUserPaginated(status, mine, new Pageable(page, size, query));
}
/**
diff --git a/wegas-core/src/test/java/com/wegas/core/ejb/GameFacadeTest.java b/wegas-core/src/test/java/com/wegas/core/ejb/GameFacadeTest.java
index c95293d6ac..7f857ede58 100644
--- a/wegas-core/src/test/java/com/wegas/core/ejb/GameFacadeTest.java
+++ b/wegas-core/src/test/java/com/wegas/core/ejb/GameFacadeTest.java
@@ -332,23 +332,32 @@ public void setupPaginated() throws Exception {
@Test
public void testFindAllGamesPaginated() {
- Page paginatedGames = gameFacade.findByStatusAndUserPaginated(Game.Status.LIVE ,new Pageable(1, 10, ""));
+ Page paginatedGames = gameFacade.findByStatusAndUserPaginated(Game.Status.LIVE, false ,new Pageable(1, 10, ""));
// We expect 3 as the superclass creates one too
Assert.assertEquals(3L, paginatedGames.getTotal());
Assert.assertTrue(paginatedGames.getPageContent().contains(game1));
Assert.assertTrue(paginatedGames.getPageContent().contains(game2));
}
+ @Test
+ public void testFindAllGamesPaginatedMine() {
+ Page paginatedGames = gameFacade.findByStatusAndUserPaginated(Game.Status.LIVE, true ,new Pageable(1, 10, ""));
+ // We expect 2 as the superclass provided game isn't ours
+ Assert.assertEquals(2L, paginatedGames.getTotal());
+ Assert.assertTrue(paginatedGames.getPageContent().contains(game1));
+ Assert.assertTrue(paginatedGames.getPageContent().contains(game2));
+ }
+
@Test
public void testFindAllGamesPaginatedFiltered() {
- Page paginatedGames = gameFacade.findByStatusAndUserPaginated(Game.Status.LIVE, new Pageable(1, 10, GAME_1));
+ Page paginatedGames = gameFacade.findByStatusAndUserPaginated(Game.Status.LIVE, false, new Pageable(1, 10, GAME_1));
Assert.assertEquals(1L, paginatedGames.getTotal());
Assert.assertTrue(paginatedGames.getPageContent().contains(game1));
}
@Test
public void testFindAllGamesPaginatedNone() {
- Page paginatedGames = gameFacade.findByStatusAndUserPaginated(Game.Status.LIVE, new Pageable(1, 10, "æøå"));
+ Page paginatedGames = gameFacade.findByStatusAndUserPaginated(Game.Status.LIVE, false, new Pageable(1, 10, "æøå"));
Assert.assertEquals(0L, paginatedGames.getTotal());
}