diff --git a/admin/src/main/ts/package.json b/admin/src/main/ts/package.json
index d415b72d91..61b285dcfc 100644
--- a/admin/src/main/ts/package.json
+++ b/admin/src/main/ts/package.json
@@ -1,6 +1,6 @@
{
"name": "admin-app",
- "version": "6.15.6",
+ "version": "6.15.6-%branch%.%generateVersion%",
"scripts": {
"ng": "ng",
"start": "ng serve --host 0.0.0.0",
@@ -34,9 +34,9 @@
"font-awesome": "4.7.0",
"jquery": "^3.4.1",
"ngx-infinite-scroll": "14.0.1",
- "ngx-ode-core": "4.7.4",
- "ngx-ode-sijil": "4.7.4",
- "ngx-ode-ui": "4.7.4",
+ "ngx-ode-core": "dev",
+ "ngx-ode-sijil": "dev",
+ "ngx-ode-ui": "dev",
"ngx-trumbowyg": "^6.0.7",
"noty": "2.4.1",
"reflect-metadata": "0.1.10",
diff --git a/communication/pom.xml b/communication/pom.xml
index 1e777cf829..344f1d59d5 100644
--- a/communication/pom.xml
+++ b/communication/pom.xml
@@ -32,5 +32,17 @@
${entCoreLibsVersion}
test
+
+ io.vertx
+ vertx-codegen
+
+
+
+
+
+ maven-compiler-plugin
+
+
+
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/controllers/CommunicationController.java b/communication/src/main/java/org/entcore/communication/controllers/CommunicationController.java
index 354241d19a..a9172e06f4 100644
--- a/communication/src/main/java/org/entcore/communication/controllers/CommunicationController.java
+++ b/communication/src/main/java/org/entcore/communication/controllers/CommunicationController.java
@@ -32,6 +32,7 @@
import fr.wseduc.webutils.http.BaseController;
import fr.wseduc.webutils.http.Renders;
import fr.wseduc.webutils.request.RequestUtils;
+import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.eventbus.Message;
import io.vertx.core.http.HttpServerRequest;
@@ -42,14 +43,32 @@
import org.entcore.common.http.filter.SuperAdminFilter;
import org.entcore.common.user.UserUtils;
import org.entcore.common.utils.StringUtils;
-import org.entcore.common.validation.StringValidation;
+import org.entcore.communication.dto.bus.CommunicationBusDTO;
+import org.entcore.communication.dto.rest.DiscoverModifyGroupUsersDTO;
+import org.entcore.communication.dto.rest.DiscoverVisibleGroupBodyDTO;
+import org.entcore.communication.dto.rest.DiscoverVisibleFilterDTO;
+import org.entcore.communication.dto.rest.CountResultDTO;
+import org.entcore.communication.dto.rest.IdentifiableDTO;
+import org.entcore.communication.dto.rest.InitDefaultRulesDTO;
+import org.entcore.communication.dto.bus.SearchVisibleBusDTO;
+import org.entcore.communication.dto.rest.SearchVisibleRequestDTO;
import org.entcore.communication.filters.CommunicationDiscoverVisibleFilter;
+import org.entcore.communication.mapper.AddLinkDirectionsDtoMapper;
+import org.entcore.communication.mapper.CommuniqueWithDtoMapper;
+import org.entcore.communication.mapper.DiscoverVisibleStructureDtoMapper;
+import org.entcore.communication.mapper.GroupDtoMapper;
+import org.entcore.communication.mapper.RemoveRelationsResultDtoMapper;
+import org.entcore.communication.mapper.VerifyResultDtoMapper;
+import org.entcore.communication.mapper.SearchVisibleContactDtoMapper;
+import org.entcore.communication.mapper.SearchVisibleDtoMapper;
+import org.entcore.communication.mapper.UserDtoMapper;
import org.entcore.communication.services.CommunicationService;
+import org.entcore.communication.mapper.BusUserDtoMapper;
import org.entcore.communication.services.impl.DefaultCommunicationService;
import java.util.List;
+import java.util.stream.Collectors;
-import static fr.wseduc.webutils.Utils.isNotEmpty;
import static org.entcore.common.http.response.DefaultResponseHandler.*;
public class CommunicationController extends BaseController {
@@ -64,6 +83,16 @@ public void adminConsole(final HttpServerRequest request) {
renderView(request);
}
+ /**
+ * Adds a communication link between two groups, upgrading the users' communication values
+ * and propagating the link to all users belonging to the concerned groups.
+ *
+ *
Deprecated — do not use. This endpoint was used by the v1 admin console
+ * and is known to corrupt data. It must not be called anymore.
+ *
+ * @param request the HTTP request containing path parameters {@code startGroupId} and {@code endGroupId}
+ * @deprecated This method can corrupt data. Use the new communication API instead.
+ */
@Post("/group/:startGroupId/communique/:endGroupId")
@SecuredAction(value = "", type = ActionType.RESOURCE)
@MfaProtected()
@@ -75,6 +104,15 @@ public void addLink(HttpServerRequest request) {
notEmptyResponseHandler(request));
}
+ /**
+ * Adds a direct communication link between two users, bypassing group-based rules.
+ *
+ * Restricted to super-admins. Intended for integration testing purposes only
+ * and should not be used in production workflows.
+ *
+ * @param request the HTTP request containing path parameters {@code startUser} and {@code endUser},
+ * and query parameter {@code direction} defining the direction of the communication link
+ */
@Put("/api/admin/users/:startUser/communiqueDirect/:endUser")
@SecuredAction(value = "", type = ActionType.RESOURCE)
@ResourceFilter(SuperAdminFilter.class)
@@ -91,6 +129,16 @@ public void setDirectCommunication(HttpServerRequest request) {
communicationService.setDirectCommunication(startUser, endUser, directionEnum, defaultResponseHandler(request));
}
+ /**
+ * Removes a communication link between two groups, downgrading the users' communication values
+ * and propagating the removal to all users belonging to the concerned groups.
+ *
+ * Deprecated — do not use. This endpoint was used by the v1 admin console
+ * and is known to corrupt data. It must not be called anymore.
+ *
+ * @param request the HTTP request containing path parameters {@code startGroupId} and {@code endGroupId}
+ * @deprecated This method can corrupt data. Use the new communication API instead.
+ */
@Delete("/group/:startGroupId/communique/:endGroupId")
@SecuredAction(value = "", type = ActionType.RESOURCE)
@MfaProtected()
@@ -102,8 +150,16 @@ public void removeLink(HttpServerRequest request) {
notEmptyResponseHandler(request));
}
/**
- * For test only
- * @param request
+ * Adds a communication link between users and a group, upgrading the group's {@code users}
+ * value (NONE, INCOMING, OUTGOING, BOTH) and propagating the link to all users belonging
+ * to the concerned group.
+ *
+ * Deprecated — do not use. This endpoint was used by the v1 admin console
+ * and is known to corrupt data. It must not be called anymore.
+ *
+ * @param request the HTTP request containing path parameter {@code groupId} and query parameter
+ * {@code direction} defining the direction of the communication link
+ * @deprecated This method can corrupt data. Use the new communication API instead.
*/
@Post("/group/:groupId")
@SecuredAction(value = "", type = ActionType.RESOURCE)
@@ -117,8 +173,16 @@ public void addLinksWithUsers(HttpServerRequest request) {
}
/**
- * For test only
- * @param request
+ * Removes a communication link between users and a group, downgrading the group's {@code users}
+ * value (NONE, INCOMING, OUTGOING, BOTH) and propagating the removal to all users belonging
+ * to the concerned group.
+ *
+ * Deprecated — do not use. This endpoint was used by the v1 admin console
+ * and is known to corrupt data. It must not be called anymore.
+ *
+ * @param request the HTTP request containing path parameter {@code groupId} and query parameter
+ * {@code direction} defining the direction of the communication link to remove
+ * @deprecated This method can corrupt data. Use the new communication API instead.
*/
@Delete("/group/:groupId")
@SecuredAction(value = "", type = ActionType.RESOURCE)
@@ -131,47 +195,136 @@ public void removeLinksWithUsers(HttpServerRequest request) {
communicationService.removeLinkWithUsers(groupId, direction, notEmptyResponseHandler(request));
}
+ /**
+ * Returns the group itself along with the list of groups referenced in its {@code communiqueWith}
+ * Neo4j property, i.e. the groups the source group can communicate with via a {@code COMMUNIQUE}
+ * relationship.
+ *
+ * @param request the HTTP request containing path parameter {@code groupId}
+ */
@Get("/group/:groupId")
@SecuredAction(value = "", type = ActionType.RESOURCE)
@MfaProtected()
public void communiqueWith(HttpServerRequest request) {
String groupId = getGroupId(request);
if (groupId == null) return;
- communicationService.communiqueWith(groupId, notEmptyResponseHandler(request));
+ communicationService.communiqueWith(groupId)
+ .onSuccess(result -> render(request, CommuniqueWithDtoMapper.map(result)))
+ .onFailure(th -> renderError(request, new JsonObject().put("error", th.getMessage())));
}
+ /**
+ * Returns the groups that the given group has outgoing {@code COMMUNIQUE} relations to,
+ * i.e. groups whose {@code users} value is {@code OUTGOING} or {@code BOTH} and that can
+ * therefore receive communication and broadcast it to their members.
+ *
+ * Response: array of {@link org.entcore.communication.dto.rest.GroupDTO}.
+ *
+ * @param request the HTTP request containing path parameter {@code groupId}
+ */
@Get("/group/:groupId/outgoing")
public void getOutgoingRelations(HttpServerRequest request) {
String groupId = request.params().get("groupId");
- communicationService.getOutgoingRelations(groupId, arrayResponseHandler(request));
+ communicationService.getOutgoingRelations(groupId)
+ .onSuccess( groups -> render(request, groups.stream().map(JsonObject.class::cast).map(GroupDtoMapper::map).collect(Collectors.toList())))
+ .onFailure(th -> renderError(request, new JsonObject().put("error", th.getMessage())));
}
+ /**
+ * Returns the groups that have incoming {@code COMMUNIQUE} relations toward the given group,
+ * i.e. groups whose {@code users} value is {@code INCOMING} or {@code BOTH} and that can
+ * therefore emit communication from their members.
+ *
+ * Response: array of {@link org.entcore.communication.dto.rest.GroupDTO}.
+ *
+ * @param request the HTTP request containing path parameter {@code groupId}
+ */
@Get("/group/:groupId/incoming")
public void getIncomingRelations(HttpServerRequest request) {
String groupId = request.params().get("groupId");
- communicationService.getIncomingRelations(groupId, arrayResponseHandler(request));
+ communicationService.getIncomingRelations(groupId)
+ .onSuccess( groups -> render(request, groups.stream().map(JsonObject.class::cast).map(GroupDtoMapper::map).collect(Collectors.toList())))
+ .onFailure(th -> renderError(request, new JsonObject().put("error", th.getMessage())));
}
+ /**
+ * Creates or updates the {@code COMMUNIQUE_DIRECT} relationship between students and their
+ * relatives for all relative in the given group. Update the relativeCommuniqueStudent group value
+ *
+ * The {@code direction} query parameter controls the direction of the link:
+ *
+ * - {@code INCOMING} — students can communicate toward their relatives
+ * - {@code OUTGOING} — relatives can communicate toward their students
+ * - {@code BOTH} — communication is allowed in both directions
+ *
+ *
+ * Response: {@link org.entcore.communication.dto.rest.CountResultDTO} with the number of
+ * affected relationships.
+ *
+ * @param request the HTTP request containing path parameter {@code groupId} and query parameter
+ * {@code direction}
+ */
@Post("/relative/:groupId")
@SecuredAction(value = "", type = ActionType.RESOURCE)
@MfaProtected()
public void addLinkBetweenRelativeAndStudent(HttpServerRequest request) {
String groupId = getGroupId(request);
- if (groupId == null) return;
+ if (groupId == null) {
+ renderError(request);
+ return;
+ }
CommunicationService.Direction direction = getDirection(request.params().get("direction"));
- communicationService.addLinkBetweenRelativeAndStudent(groupId, direction, notEmptyResponseHandler(request));
+ communicationService.addLinkBetweenRelativeAndStudent(groupId, direction)
+ .onSuccess( count -> render(request, new CountResultDTO(count)))
+ .onFailure(th -> renderError(request, new JsonObject().put("error", th.getMessage())));
}
+ /**
+ * Removes the {@code COMMUNIQUE_DIRECT} relationship between students and their relatives
+ * for all student/relative pairs in the given group.
+ *
+ * The {@code direction} query parameter controls which link is removed:
+ *
+ * - {@code INCOMING} — removes the link from students toward their relatives
+ * - {@code OUTGOING} — removes the link from relatives toward their students
+ * - {@code BOTH} — removes links in both directions
+ *
+ *
+ * Response: {@link org.entcore.communication.dto.rest.CountResultDTO} with the number of
+ * affected relationships.
+ *
+ * @param request the HTTP request containing path parameter {@code groupId} and query parameter
+ * {@code direction}
+ */
@Delete("/relative/:groupId")
@SecuredAction(value = "", type = ActionType.RESOURCE)
@MfaProtected()
public void removeLinkBetweenRelativeAndStudent(HttpServerRequest request) {
String groupId = getGroupId(request);
- if (groupId == null) return;
+ if (groupId == null) {
+ renderError(request);
+ return;
+ }
CommunicationService.Direction direction = getDirection(request.params().get("direction"));
- communicationService.removeLinkBetweenRelativeAndStudent(groupId, direction, notEmptyResponseHandler(request));
+ communicationService.removeLinkBetweenRelativeAndStudent(groupId, direction)
+ .onSuccess( count -> render(request, new CountResultDTO(count)))
+ .onFailure(th -> renderError(request, new JsonObject().put("error", th.getMessage())));
}
+ /**
+ * Retrieves all users and groups visible to the given user.
+ *
+ * Route: {@code GET /visible/:userId}
+ *
+ * Query parameters:
+ *
+ * - {@code schoolId} — optional school context to filter results
+ * - {@code expectedType} — optional, repeatable; restricts results to specific profile types
+ *
+ *
+ * @param request the HTTP request; path param {@code userId} identifies the user whose visible contacts are fetched
+ * @deprecated This endpoint no longer appears to be called. Consider removing it or replacing it with the POST {@code /visible} endpoint.
+ */
@Get("/visible/:userId")
@SecuredAction("communication.visible.user")
public void visibleUsers(final HttpServerRequest request) {
@@ -185,135 +338,56 @@ public void visibleUsers(final HttpServerRequest request) {
}
}
+ /**
+ * Searches for all users and groups visible to the authenticated user, applying the filters provided in the request body.
+ *
+ * Route: {@code POST /visible}
+ *
+ * The request body is deserialized into a {@link SearchVisibleRequestDTO}. The following fields are always
+ * overridden server-side regardless of client input:
+ *
+ * - {@code itSelf=true} — the caller is always included
+ * - {@code myGroup=true} — the caller's own groups are always included
+ * - {@code profile=false} — profile-only entries are excluded
+ *
+ * Available filters in the body: {@code structures}, {@code classes}, {@code profiles}, {@code functions},
+ * {@code positions}, {@code search}, {@code types}, {@code nbUsersInGroups}, {@code groupType}.
+ *
+ * Returns a {@link org.entcore.communication.dto.rest.SearchVisibleResultDTO} containing:
+ *
+ * - {@code users} — list of {@link org.entcore.communication.dto.rest.UserDTO} visible to the caller
+ * - {@code groups} — list of {@link org.entcore.communication.dto.rest.GroupDTO} visible to the caller,
+ * optionally annotated with their group type when {@code groupType=true}
+ *
+ *
+ * @param request the HTTP request carrying the JSON body with search filters
+ */
@Post("/visible")
@SecuredAction(value = "", type = ActionType.AUTHENTICATED)
public void searchVisible(HttpServerRequest request) {
- RequestUtils.bodyToJson(request, filter -> UserUtils.getUserInfos(eb, request, user -> {
- if (user != null) {
- String preFilter = "";
- String match = "";
- String where = "";
- String nbUsers = "";
- String groupTypes = "";
- JsonObject params = new JsonObject();
- JsonArray expectedTypes = null;
- if (filter != null && filter.size()> 0) {
- for (String criteria : filter.fieldNames()) {
- switch (criteria) {
- case "structures":
- case "classes":
- JsonArray itemssc = filter.getJsonArray(criteria);
- if (itemssc == null || itemssc.isEmpty() ||
- ("structures".equals(criteria) && filter.getJsonArray("classes") != null &&
- !filter.getJsonArray("classes").isEmpty())) continue;
- if (!params.containsKey("nIds")) {
- params.put("nIds", itemssc);
- } else {
- params.getJsonArray("nIds").addAll(itemssc);
- }
- if (!match.contains("-[:DEPENDS")) {
- if (!match.contains("MATCH ")) {
- match = "MATCH ";
- where = " WHERE ";
- } else {
- // We use another MATCH here, because for users not attached to a class, a simple comma does some oddities
- match += " MATCH ";
- where += "AND ";
- }
- match += "(visibles)-[:IN*0..1]->()-[:DEPENDS]->(n) ";
- where += "n.id IN {nIds} ";
- }
- if ("structures".equals(criteria)) {
- match = match.replaceFirst("\\[:DEPENDS]", "[:DEPENDS*1..2]");
- }
- break;
- case "profiles":
- case "functions":
- JsonArray itemspf = filter.getJsonArray(criteria);
- if (itemspf == null || itemspf.isEmpty()) continue;
- if (!params.containsKey("filters")) {
- params.put("filters", itemspf);
- } else {
- //params.getJsonArray("filters").addAll(itemspf);
- params.put("filters2", itemspf);
- }
- if (!match.contains("MATCH ")) {
- match = "MATCH ";
- where = " WHERE ";
- } else {
- // We use another MATCH here, because for users not attached to a class, a simple comma does some oddities
- match += " MATCH ";
- where += "AND ";
- }
- if (!match.contains("(visibles)-[:IN*0..1]->(g)")) {
- match += "(visibles)-[:IN*0..1]->(g)";
- where += "g.filter IN {filters} ";
- } else {
- match += "(visibles)-[:IN*0..1]->(g2) ";
- where += "g2.filter IN {filters2} ";
- }
- break;
- case "positions":
- JsonArray positionIds = filter.getJsonArray(criteria);
- if (positionIds == null || positionIds.isEmpty()) continue;
- params.put("positionIds", positionIds);
- if (!match.contains("MATCH ")) {
- where = " WHERE ";
- } else {
- where += "AND ";
- }
- where += " ANY(id IN positionIds WHERE id IN {positionIds}) ";
- break;
- case "search":
- final String search = filter.getString(criteria);
- if (isNotEmpty(search)) {
- preFilter = "AND m.displayNameSearchField CONTAINS {search} ";
-
- String sanitizedSearch = StringValidation.sanitize(search);
- params.put("search", sanitizedSearch);
- }
- break;
- case "types":
- JsonArray types = filter.getJsonArray(criteria);
- if (types != null && types.size() > 0 && CommunicationService.EXPECTED_TYPES.containsAll(types.getList())) {
- expectedTypes = types;
- }
- break;
- case "nbUsersInGroups":
- if (filter.getBoolean("nbUsersInGroups", false)) {
- nbUsers = ", visibles.nbUsers as nbUsers";
- }
- break;
- case "groupType":
- if (filter.getBoolean("groupType", false)) {
- groupTypes = ", labels(visibles) as groupType, visibles.filter as groupProfile";
- }
- break;
- }
- }
- }
- final boolean returnGroupType = !groupTypes.isEmpty();
- final String customReturn = match + where +
- "RETURN DISTINCT visibles.id as id, visibles.name as name, " +
- "positionNames, positionIds, " +
- "visibles.displayName as displayName, visibles.groupDisplayName as groupDisplayName, " +
- "HEAD(visibles.profiles) as profile, subjects" + nbUsers + groupTypes;
- communicationService.visibleUsers(user.getUserId(), null, expectedTypes, true, true, false,
- preFilter, customReturn, params, user.getType(), false, visibles -> {
- if (visibles.isRight()) {
- renderJson(request,
- UserUtils.translateAndGroupVisible(visibles.right().getValue(),
- I18n.acceptLanguage(request), returnGroupType));
- } else {
- leftToResponse(request, visibles.left());
- }
- });
- } else {
- badRequest(request, "invalid.user");
- }
+ RequestUtils.bodyToClass(request, SearchVisibleRequestDTO.class).onSuccess(rawFilter -> UserUtils.getUserInfos(eb, request, user -> {
+ SearchVisibleRequestDTO filter = rawFilter.withServerDefaults();
+ boolean returnGroupType = Boolean.TRUE.equals(filter.getGroupType());
+ communicationService.visibleUsers(user, filter)
+ .onFailure(th -> renderError(request, new JsonObject().put("error", th.getMessage())))
+ .onSuccess( visibles -> {
+ render(request, SearchVisibleDtoMapper.map(UserUtils.translateAndGroupVisible(visibles, I18n.acceptLanguage(request), returnGroupType)));
+ });
}));
}
+ /**
+ * Lists all users in a given group that are visible to the currently authenticated user.
+ *
+ * Visibility is determined by the communication rules: only users the connected user
+ * is allowed to communicate with are returned, even if they belong to the group.
+ *
+ * @param request the HTTP request; must contain a {@code groupId} path parameter
+ * identifying the group whose members are to be listed
+ * @return a list of user objects {@link org.entcore.communication.dto.rest.UserDTO},
+ * each containing {@code id}, {@code displayName}, {@code login}, and {@code type} fields,
+ * ordered by profile type (descending) then display name
+ */
@Get("/visible/group/:groupId")
@SecuredAction(value = "", type = ActionType.AUTHENTICATED)
public void visibleGroupContains(HttpServerRequest request) {
@@ -324,19 +398,29 @@ public void visibleGroupContains(HttpServerRequest request) {
badRequest(request, "invalid.groupId");
return;
}
+ //FIXME we must use a dedicated query to prefilter on users of a group
String customReturn =
"WITH COLLECT(visibles.id) as vIds " +
- "UNWIND vIds AS vId "+
- "MATCH (u:User { id: vId }) "+
- "USING INDEX u:User(id) " +
- "MATCH (s:Group { id : {groupId}})<-[:IN]-(u:User) " +
- "USING INDEX s:Group(id) "+
- "RETURN DISTINCT HEAD(u.profiles) as type, u.id as id, " +
- "u.displayName as displayName, u.login as login " +
- "ORDER BY type DESC, displayName ";
+ "UNWIND vIds AS vId "+
+ "MATCH (u:User { id: vId }) "+
+ "USING INDEX u:User(id) " +
+ "MATCH (s:Group { id : {groupId}})<-[:IN]-(u:User) " +
+ "USING INDEX s:Group(id) "+
+ "RETURN DISTINCT HEAD(u.profiles) as type, u.id as id, " +
+ "u.displayName as displayName, u.login as login " +
+ "ORDER BY type DESC, displayName ";
final JsonObject params = new JsonObject().put("groupId", groupId);
communicationService.visibleUsers(user.getUserId(), null, null, true, true, false, null,
- customReturn, params, arrayResponseHandler(request));
+ customReturn, params, visibles -> {
+ if(visibles.isLeft()) {
+ renderError(request, new JsonObject().put("error", visibles.left()));
+ return;
+ }
+ render(request, visibles.right().getValue().stream()
+ .map(JsonObject.class::cast)
+ .map(UserDtoMapper::map)
+ .collect(Collectors.toList()));
+ });
} else {
badRequest(request, "invalid.user");
}
@@ -345,65 +429,56 @@ public void visibleGroupContains(HttpServerRequest request) {
@BusAddress("wse.communication.users")
public void visibleUsers(final Message message) {
- String userId = message.body().getString("userId");
- if (userId != null && !userId.trim().isEmpty()) {
- String action = message.body().getString("action", "");
- String schoolId = message.body().getString("schoolId");
- JsonArray expectedTypes = message.body().getJsonArray("expectedTypes");
- Handler> responseHandler = new Handler>() {
-
- @Override
- public void handle(Either res) {
- JsonArray j;
- if (res.isRight()) {
- j = res.right().getValue();
- } else {
- log.warn(res.left().getValue());
- j = new JsonArray();
- }
- message.reply(j);
- }
- };
- switch (action) {
+ SearchVisibleBusDTO searchQuery = new SearchVisibleBusDTO(message.body());
+ String userId = searchQuery.getUserId();
+ if (userId == null || userId.trim().isEmpty()) {
+ message.reply(new JsonArray());
+ return;
+ }
+
+ String schoolId = searchQuery.getSchoolId();
+ JsonArray expectedTypes = new JsonArray(searchQuery.getExpectedTypes());
+ final Future future;
+
+ switch (searchQuery.getAction()) {
case "visibleUsers":
- String preFilter = message.body().getString("preFilter");
- String customReturn = message.body().getString("customReturn");
- JsonObject ap = message.body().getJsonObject("additionnalParams");
- boolean itSelf = message.body().getBoolean("itself", false);
- boolean myGroup = communicationService instanceof DefaultCommunicationService ? true :
- message.body().getBoolean("mygroup", false);
- boolean profile = message.body().getBoolean("profile", true);
- String userProfile = message.body().getString("userProfile", null);
- boolean reverseUnion = message.body().getBoolean("reverseUnion", false);
- communicationService.visibleUsers(userId, schoolId, expectedTypes, itSelf, myGroup,
- profile, preFilter, customReturn, ap, userProfile, reverseUnion, responseHandler);
+ boolean myGroup = communicationService instanceof DefaultCommunicationService || searchQuery.isMyGroup();
+ //FIXME: we can't define a DTO until we remove customeReturn from query
+ future = communicationService.visibleUsers(userId, schoolId, expectedTypes,
+ searchQuery.isItSelf(), myGroup, searchQuery.isProfile(),
+ searchQuery.getPreFilter(), searchQuery.getCustomReturn(),
+ new JsonObject(searchQuery.getAdditionnalParams()),
+ searchQuery.getUserProfile(), searchQuery.isReverseUnion());
break;
case "visibleUsersForShare":
- String search = message.body().getString("search");
- JsonArray userIds = message.body().getJsonArray("userIds");
- communicationService.visibleUsersForShare(userId, search, userIds, responseHandler);
- break;
+ future = communicationService.visibleUsersForShare(userId, searchQuery.getSearch(),
+ new JsonArray(searchQuery.getUserIds()))
+ .map(arr -> serializeForBus(arr, BusUserDtoMapper::map));
+ break;
case "usersCanSeeMe":
- communicationService.usersCanSeeMe(userId, responseHandler);
+ future = communicationService.usersCanSeeMe(userId)
+ .map(arr -> serializeForBus(arr, BusUserDtoMapper::map));
break;
case "visibleProfilsGroups":
- String pF = message.body().getString("preFilter");
- String c = message.body().getString("customReturn");
- JsonObject p = message.body().getJsonObject("additionnalParams");
- communicationService.visibleProfilsGroups(userId, c, p, pF, responseHandler);
+ //FIXME: we can't define a DTO until we remove customeReturn from query
+ future = communicationService.visibleProfilsGroups(userId, searchQuery.getCustomReturn(),
+ new JsonObject(searchQuery.getAdditionnalParams()), searchQuery.getPreFilter());
break;
case "visibleManualGroups":
- String cr = message.body().getString("customReturn");
- JsonObject pa = message.body().getJsonObject("additionnalParams");
- communicationService.visibleManualGroups(userId, cr, pa, responseHandler);
+ //FIXME: we can't define a DTO until we remove customeReturn from query
+ future = communicationService.visibleManualGroups(userId, searchQuery.getCustomReturn(),
+ new JsonObject(searchQuery.getAdditionnalParams()));
break;
default:
message.reply(new JsonArray());
- break;
- }
- } else {
- message.reply(new JsonArray());
+ return;
}
+
+ future.onSuccess(message::reply)
+ .onFailure(err -> {
+ log.warn(err.getMessage());
+ message.reply(new JsonArray());
+ });
}
private void visibleUsers(String userId, String schoolId, JsonArray expectedTypes,
@@ -417,24 +492,38 @@ private void visibleUsers(String userId, String schoolId, JsonArray expectedType
userId, schoolId, expectedTypes, itSelf, false, true, null, customReturn, additionnalParams, handler);
}
+ /**
+ * Initializes default communication rules for the specified structures.
+ *
+ * Rules are read from the {@code initDefaultCommunicationRules} entry of the module configuration.
+ *
+ *
Response: {@code 200 OK} with an empty JSON object {@code {}} on success.
+ *
+ * @param request the HTTP request containing a {@link InitDefaultRulesDTO} JSON body
+ */
@Put("/init/rules")
@SecuredAction("communication.init.default.rules")
public void initDefaultCommunicationRules(final HttpServerRequest request) {
- RequestUtils.bodyToJson(request, new Handler() {
- @Override
- public void handle(JsonObject body) {
- JsonObject initDefaultRules = config.getJsonObject("initDefaultCommunicationRules");
- JsonArray structures = body.getJsonArray("structures");
- if (structures != null && structures.size() > 0) {
- communicationService.initDefaultRules(structures,
- initDefaultRules, defaultResponseHandler(request));
- } else {
- badRequest(request, "invalid.structures");
- }
+ RequestUtils.bodyToClass(request, InitDefaultRulesDTO.class).onSuccess(body -> {
+ if (body.getStructures() == null || body.getStructures().isEmpty()) {
+ badRequest(request, "invalid.structures");
+ return;
}
- });
+ JsonObject initDefaultRules = config.getJsonObject("initDefaultCommunicationRules");
+ communicationService.initDefaultRules(new JsonArray(body.getStructures()), initDefaultRules)
+ .onSuccess(result -> renderJson(request, result))
+ .onFailure(err -> renderError(request, new JsonObject().put("error", err.getMessage())));
+ }).onFailure(err -> badRequest(request, err.getMessage()));
}
+ /**
+ * Resets the communication rules of a structure to their default values.
+ *
+ * Restricted to super-admins.
+ *
+ * @param request the HTTP request; must contain a {@code structureId} path parameter
+ * identifying the structure whose rules are to be reset
+ */
@Post("/rules/:structureId/reset")
@SecuredAction(value = "", type = ActionType.RESOURCE)
@ResourceFilter(SuperAdminFilter.class)
@@ -457,6 +546,16 @@ public void getDefaultCommunicationRules(final HttpServerRequest request) {
Renders.renderJson(request, initDefaultRules, 200);
}
+ /**
+ * Applies the default communication rules to a structure.
+ *
+ * Only adds the rules defined as defaults — rules that already exist but are not part of
+ * the defaults are left untouched and will not be removed.
+ * To fully reset a structure's rules to defaults, use {@link #resetRules} instead.
+ *
+ * @param request the HTTP request; must contain a {@code structureId} path parameter
+ * identifying the structure to apply the default rules to
+ */
@Put("/rules/:structureId")
@SecuredAction("communication.default.rules")
public void defaultCommunicationRules(final HttpServerRequest request) {
@@ -471,110 +570,92 @@ public void defaultCommunicationRules(final HttpServerRequest request) {
@BusAddress("wse.communication")
public void communicationEventBusHandler(final Message message) {
+ CommunicationBusDTO dto = new CommunicationBusDTO(message.body());
JsonObject initDefaultRules = config.getJsonObject("initDefaultCommunicationRules");
- final Handler> responseHandler = new Handler>() {
+ final Future future;
- @Override
- public void handle(Either res) {
- if (res.isRight()) {
- message.reply(res.right().getValue());
- } else {
- message.reply(new JsonObject().put("status", "error")
- .put("message", res.left().getValue()));
- }
- }
- };
- switch (message.body().getString("action", "")) {
- case "initDefaultCommunicationRules" :
- communicationService.initDefaultRules(message.body().getJsonArray("schoolIds"),
- initDefaultRules, responseHandler);
+ switch (dto.getAction() != null ? dto.getAction() : "") {
+ case "initDefaultCommunicationRules":
+ future = communicationService.initDefaultRules(new JsonArray(dto.getSchoolIds()), initDefaultRules);
break;
- case "initAndApplyDefaultCommunicationRules" :
- final Integer transactionId = message.body().getInteger("transactionId");
- final Boolean commit = message.body().getBoolean("commit", true);
- communicationService.initDefaultRules(message.body().getJsonArray("schoolIds"),
- initDefaultRules, transactionId, commit, new Handler>() {
- @Override
- public void handle(Either event) {
- if (event.isRight()) {
- communicationService.applyDefaultRules(message.body().getJsonArray("schoolIds"),
- transactionId, commit, responseHandler);
- } else {
- message.reply(new JsonObject().put("status", "error")
- .put("message", event.left().getValue()));
- }
- }
- });
+ case "initAndApplyDefaultCommunicationRules":
+ future = communicationService.initDefaultRules(new JsonArray(dto.getSchoolIds()), initDefaultRules,
+ dto.getTransactionId(), dto.isCommit())
+ .compose(ignored -> communicationService.applyDefaultRules(new JsonArray(dto.getSchoolIds()),
+ dto.getTransactionId(), dto.isCommit()));
break;
- case "setDefaultCommunicationRules" :
- communicationService.applyDefaultRules(new JsonArray().add(
- message.body().getString("schoolId")), responseHandler);
+ case "setDefaultCommunicationRules":
+ future = communicationService.applyDefaultRules(new JsonArray().add(dto.getSchoolId()));
break;
- case "setMultipleDefaultCommunicationRules" :
- communicationService.applyDefaultRules(
- message.body().getJsonArray("schoolIds"), responseHandler);
+ case "setMultipleDefaultCommunicationRules":
+ future = communicationService.applyDefaultRules(new JsonArray(dto.getSchoolIds()));
break;
- case "setCommunicationRules" :
- communicationService.applyRules(
- message.body().getString("groupId"), responseHandler);
+ case "setCommunicationRules":
+ future = communicationService.applyRules(dto.getGroupId());
break;
- case "addLink" :
- // Create communication link between two groups
- if (message.body().containsKey("startGroupId") && message.body().containsKey("endGroupId")) {
- String startGroupId = message.body().getString("startGroupId");
- String endGroupId = message.body().getString("endGroupId");
-
- communicationService.addLink(startGroupId, endGroupId, responseHandler);
+ case "addLink":
+ if (dto.getStartGroupId() == null || dto.getEndGroupId() == null) {
+ future = Future.failedFuture("missing.parameters");
} else {
- message.reply(new JsonObject().put("status", "error")
- .put("message", "missing.parameters"));
+ future = communicationService.addLink(dto.getStartGroupId(), dto.getEndGroupId());
}
break;
- case "addLinkWithUsers" :
- if (message.body().containsKey("groupId") && message.body().containsKey("direction")) {
- String groupId = message.body().getString("groupId");
- String direction = message.body().getString("direction", "BOTH");
- CommunicationService.Direction directionEnum;
- try {
- directionEnum = CommunicationService.Direction.valueOf(direction.toUpperCase());
- } catch (IllegalArgumentException | NullPointerException e) {
- directionEnum = CommunicationService.Direction.BOTH;
- }
-
- communicationService.addLinkWithUsers(groupId, directionEnum, responseHandler);
- }else {
- message.reply(new JsonObject().put("status", "error")
- .put("message", "missing.parameters"));
+ case "addLinkWithUsers":
+ if (dto.getGroupId() == null || dto.getDirection() == null) {
+ future = Future.failedFuture("missing.parameters");
+ } else {
+ future = communicationService.addLinkWithUsers(dto.getGroupId(), parseDirection(dto.getDirection()));
}
break;
- case "removeLinkWithUsers" :
- if (message.body().containsKey("groupId") && message.body().containsKey("direction")) {
- String groupId = message.body().getString("groupId");
- String direction = message.body().getString("direction", "BOTH");
- CommunicationService.Direction directionEnum;
- try {
- directionEnum = CommunicationService.Direction.valueOf(direction.toUpperCase());
- } catch (IllegalArgumentException | NullPointerException e) {
- directionEnum = CommunicationService.Direction.BOTH;
- }
-
- communicationService.removeLinkWithUsers(groupId, directionEnum, responseHandler);
+ case "removeLinkWithUsers":
+ if (dto.getGroupId() == null || dto.getDirection() == null) {
+ future = Future.failedFuture("missing.parameters");
} else {
- message.reply(new JsonObject().put("status", "error")
- .put("message", "missing.parameters"));
+ future = communicationService.removeLinkWithUsers(dto.getGroupId(), parseDirection(dto.getDirection()));
}
break;
default:
- message.reply(new JsonObject().put("status", "error")
- .put("message", "invalid.action"));
+ future = Future.failedFuture("invalid.action");
+ }
+
+ future
+ .onSuccess(message::reply)
+ .onFailure(err -> message.reply(new JsonObject().put("status", "error").put("message", err.getMessage())));
+ }
+
+ private static CommunicationService.Direction parseDirection(String direction) {
+ try {
+ return CommunicationService.Direction.valueOf(direction.toUpperCase());
+ } catch (IllegalArgumentException | NullPointerException e) {
+ return CommunicationService.Direction.BOTH;
}
}
+ /**
+ * Removes all communication rules of profile groups belonging to the given structure,
+ * including direct ({@code communiqueDirect}) links between users of those profile groups.
+ *
+ * Other group types (manual groups, etc.) and inbound links from groups outside the structure
+ * pointing to the structure's profile groups are not affected.
+ *
+ *
Deprecated — do not use. Because only a subset of rules and links is
+ * removed, the {@code communiqueWith} and {@code users} values on the affected Neo4j group nodes
+ * are not recalculated after the deletion. This leaves those values stale and can lead to data
+ * corruption. Use {@link #resetRules} to safely reset a structure's rules to their default state.
+ *
+ * @param request the HTTP request; must contain a {@code structureId} query parameter
+ * identifying the structure whose profile-group rules are to be removed
+ * @deprecated Can corrupt {@code communiqueWith} / {@code users} values in Neo4j.
+ * Use {@link #resetRules} instead.
+ */
+ @Deprecated
@Delete("/rules")
@SecuredAction("communication.remove.rules")
public void removeCommunicationRules(HttpServerRequest request) {
String structureId = request.params().get("structureId");
- communicationService.removeRules(structureId, defaultResponseHandler(request));
+ communicationService.removeRules(structureId)
+ .onSuccess(v -> Renders.ok(request))
+ .onFailure(th -> renderError(request, new JsonObject().put("error", th.getMessage())));
}
public void setCommunicationService(CommunicationService communicationService) {
@@ -636,42 +717,76 @@ private String getGroupId(HttpServerRequest request) {
return groupId;
}
+ /**
+ * Sets the communication direction of a group to {@code BOTH} and adds the corresponding
+ * communication links between the group and its users if they do not already exist.
+ *
+ * Used by the v2 admin console to grant two-way communication between a group and its members.
+ *
+ * @param request the HTTP request; must contain a {@code groupId} path parameter
+ * identifying the group to update
+ * @return {@code 200 OK} with a {@link GroupUsersDirectionDTO} body confirming the applied direction
+ */
@Post("/group/:groupId/users")
@SecuredAction(value = "", type = ActionType.RESOURCE)
@MfaProtected()
public void safelyAddLinksWithUsers(HttpServerRequest request) {
String groupId = getGroupId(request);
if (groupId == null) return;
- communicationService.addLinkWithUsers(groupId, CommunicationService.Direction.BOTH, event -> {
- if (event.isRight()) {
- Renders.renderJson(request, new JsonObject().put("users", CommunicationService.Direction.BOTH), 200);
- } else {
- JsonObject error = new JsonObject().put("error", event.left().getValue());
- Renders.renderJson(request, error, 400);
- }
- });
+ communicationService.addLinkWithUsers(groupId, CommunicationService.Direction.BOTH)
+ .onSuccess(v -> Renders.renderJson(request, new JsonObject().put("users", CommunicationService.Direction.BOTH), 200))
+ .onFailure(th -> renderJson(request, new JsonObject().put("error", th.getMessage()), 400));
}
+ /**
+ * Safely downgrades the communication direction of a group by removing its link with users,
+ * if doing so is consistent with the group's existing inbound and outbound relations.
+ *
+ * The service counts how many other groups send to ({@code numberOfSendingGroups}) and receive
+ * from ({@code numberOfReceivingGroups}) this group, then computes the minimum direction still
+ * required. For example, a group whose {@code users} value is {@code BOTH} but only receives
+ * communication from other groups can be safely downgraded to {@code OUTGOING}
+ * (meaning the group receives communication and dispatches it to its members).
+ *
+ * If no safe downgrade exists, the operation is refused.
+ *
+ * @param request the HTTP request; must contain a {@code groupId} path parameter
+ * identifying the group to downgrade
+ * @return {@code 200 OK} with a {@link GroupUsersDirectionDTO} body containing the resulting direction,
+ * {@code 409 Conflict} if the direction cannot be safely changed,
+ * or {@code 500} on unexpected error
+ */
@Delete("/group/:groupId/users")
@SecuredAction(value = "", type = ActionType.RESOURCE)
@MfaProtected()
public void safelyRemoveLinksWithUsers(HttpServerRequest request) {
String groupId = getGroupId(request);
if (groupId == null) return;
- communicationService.safelyRemoveLinkWithUsers(groupId, event -> {
- if (event.isLeft()) {
- String error = event.left().getValue();
- if (error.equals(CommunicationService.IMPOSSIBLE_TO_CHANGE_DIRECTION)) {
- Renders.renderJson(request, new JsonObject().put("error", error), 409);
- } else {
- Renders.renderJson(request, new JsonObject().put("error", error), 500);
- }
- } else {
- Renders.renderJson(request, event.right().getValue(), 200);
- }
- });
+ communicationService.safelyRemoveLinkWithUsers(groupId)
+ .onSuccess(result -> Renders.renderJson(request, result, 200))
+ .onFailure(th -> {
+ int status = CommunicationService.IMPOSSIBLE_TO_CHANGE_DIRECTION.equals(th.getMessage()) ? 409 : 500;
+ renderJson(request, new JsonObject().put("error", th.getMessage()), status);
+ });
}
+ /**
+ * Checks whether a communication link from {@code startGroupId} to {@code endGroupId} can be
+ * added by the authenticated user, without actually creating the link.
+ *
+ * The check takes into account the current {@code users} direction of both groups and the
+ * caller's role. For example, an ADML cannot add a link {@code g1 -> g2} if {@code g2} is
+ * currently {@code INCOMING}, because doing so would require upgrading {@code g2} to
+ * {@code BOTH} — an operation restricted to super-admins for certain group types.
+ *
+ * @param request the HTTP request; must contain path parameters {@code startGroupId} and
+ * {@code endGroupId} identifying the source and target groups
+ * @return {@code 200 OK} with a JSON body {@code {"warning": ""}} where {@code warning}
+ * is a non-null string if the link is possible but carries a notable side-effect,
+ * or {@code null} if the link can be added without any caveat;
+ * {@code 409 Conflict} if the link cannot be added at all given the caller's permissions,
+ * or {@code 500} on unexpected error
+ */
@Get("/v2/group/:startGroupId/communique/:endGroupId/check")
@ResourceFilter(AdminFilter.class)
@SecuredAction(value = "", type = ActionType.RESOURCE)
@@ -680,22 +795,29 @@ public void addLinkCheckOnly(HttpServerRequest request) {
Params params = new Params(request).validate();
if (params.isInvalid()) return;
- UserUtils.getUserInfos(eb, request, user -> {
- communicationService.addLinkCheckOnly(params.getStartGroupId(), params.getEndGroupId(), user, event -> {
- if (event.isLeft()) {
- String error = event.left().getValue();
- if (CommunicationService.IMPOSSIBLE_TO_CHANGE_DIRECTION.equals(error)) {
- Renders.renderJson(request, new JsonObject().put("error", error), 409);
- } else {
- Renders.renderJson(request, new JsonObject().put("error", error), 500);
- }
- } else {
- Renders.renderJson(request, event.right().getValue(), 200);
- }
- });
- });
+ UserUtils.getAuthenticatedUserInfos(eb, request)
+ .compose(user -> communicationService.addLinkCheckOnly(params.getStartGroupId(), params.getEndGroupId(), user))
+ .onSuccess(result -> Renders.renderJson(request, result, 200))
+ .onFailure(th -> {
+ int status = CommunicationService.IMPOSSIBLE_TO_CHANGE_DIRECTION.equals(th.getMessage()) ? 409 : 500;
+ renderJson(request, new JsonObject().put("error", th.getMessage()), status);
+ });
}
+ /**
+ * Adds a communication link from {@code startGroupId} to {@code endGroupId} and adjusts the
+ * {@code users} direction of each group if necessary to reflect the new relation.
+ *
+ * The operation is refused if adding the link would require a direction upgrade that the
+ * authenticated user is not permitted to perform (e.g. an ADML upgrading a restricted group
+ * to {@code BOTH}). Use {@link #addLinkCheckOnly} first to verify feasibility without side effects.
+ *
+ * @param request the HTTP request; must contain path parameters {@code startGroupId} and
+ * {@code endGroupId} identifying the source and target groups
+ * @return {@code 200 OK} with an {@link AddLinkDirectionsDTO} body containing the resulting
+ * direction of each group after the link was applied,
+ * or {@code 500} if the operation failed or was not permitted
+ */
@Post("/v2/group/:startGroupId/communique/:endGroupId")
@ResourceFilter(AdminFilter.class)
@SecuredAction(value = "", type = ActionType.RESOURCE)
@@ -704,28 +826,34 @@ public void processAddLinkAndChangeDirection(HttpServerRequest request) {
Params params = new Params(request).validate();
if (params.isInvalid()) return;
- communicationService.addLink(params.getStartGroupId(), params.getEndGroupId(), event -> {
- if (event.isLeft()) {
- Renders.renderJson(request, new JsonObject().put("error", event.left().getValue()), 500);
- } else {
- communicationService.processChangeDirectionAfterAddingLink(params.getStartGroupId(), params.getEndGroupId(), eventChange -> {
- if (event.isLeft()) {
- Renders.renderJson(request, new JsonObject().put("error", event.left().getValue()), 500);
- } else {
- Renders.renderJson(request, eventChange.right().getValue(), 200);
- }
- });
- }
- });
+ communicationService.addLink(params.getStartGroupId(), params.getEndGroupId())
+ .compose(v -> communicationService.processChangeDirectionAfterAddingLink(params.getStartGroupId(), params.getEndGroupId()))
+ .onSuccess(result -> render(request, AddLinkDirectionsDtoMapper.map(result)))
+ .onFailure(th -> renderJson(request, new JsonObject().put("error", th.getMessage()), 500));
}
+ /**
+ * Removes the communication relation between two groups and recalculates the minimal
+ * {@code users} direction required for each group based on their remaining communication links.
+ *
+ * After the link is removed, the service inspects all still-existing inbound and outbound
+ * relations of both groups and downgrades their direction to the lowest value that still
+ * satisfies those remaining relations.
+ *
+ * @param request the HTTP request; must contain path parameters {@code startGroupId} and
+ * {@code endGroupId} identifying the sender and receiver groups
+ * @return {@code 200 OK} with a {@link RemoveRelationsResultDTO} body containing the
+ * recalculated direction of the sender and the receiver after the removal
+ */
@Delete("/group/:startGroupId/relations/:endGroupId")
@SecuredAction(value = "", type = ActionType.RESOURCE)
@MfaProtected()
public void removeRelations(HttpServerRequest request) {
Params params = new Params(request).validate();
if (params.isInvalid()) return;
- communicationService.removeRelations(params.getStartGroupId(), params.getEndGroupId(), notEmptyResponseHandler(request));
+ communicationService.removeRelations(params.getStartGroupId(), params.getEndGroupId())
+ .onSuccess(result -> render(request, RemoveRelationsResultDtoMapper.map(result)))
+ .onFailure(th -> renderError(request, new JsonObject().put("error", th.getMessage())));
}
/**
@@ -750,70 +878,87 @@ public void verify(HttpServerRequest request) {
badRequest(request, "invalid.parameter");
return;
}
- communicationService.verify(from, to, notEmptyResponseHandler(request));
+ communicationService.verify(from, to)
+ .onSuccess(result -> render(request, VerifyResultDtoMapper.map(result)))
+ .onFailure(th -> renderError(request, new JsonObject().put("error", th.getMessage())));
}
/**
- * Discover visible users, return list of users that can be dicover by the user
- * @param request
- * {
- * "structures": ["id1", "id2"],
- * "profiles": ["Teacher"],
- * "search": "search",
- * }
+ * Returns the list of users discoverable by the authenticated user, optionally filtered by
+ * structures, profiles, and a search string.
+ *
+ * Normandie only — this endpoint is part of the discover-network feature
+ * and is only active in the Normandie deployment.
*
- * */
+ * @param request the HTTP request containing a {@link DiscoverVisibleFilterDTO} JSON body
+ * @return {@code 200 OK} with a JSON array of {@link UserDTO} entries
+ */
@Post("/discover/visible/users")
@SecuredAction(value= "", type = ActionType.RESOURCE)
@ResourceFilter(CommunicationDiscoverVisibleFilter.class)
public void getDiscoverVisibleUsers(HttpServerRequest request) {
- RequestUtils.bodyToJson(request, filter -> UserUtils.getUserInfos(eb, request, user -> {
- if(user == null) {
- badRequest(request, "invalid.user");
- return;
- }
- communicationService.getDiscoverVisibleUsers( user.getUserId(), filter, arrayResponseHandler(request));
- }));
+ RequestUtils.bodyToClass(request, DiscoverVisibleFilterDTO.class)
+ .compose(filter -> UserUtils.getAuthenticatedUserInfos(eb, request)
+ .compose(user -> communicationService.getDiscoverVisibleUsers(user.getUserId(), filter)))
+ .onSuccess(results -> render(request, results.stream()
+ .map(JsonObject.class::cast)
+ .map(UserDtoMapper::mapDiscoverVisible)
+ .collect(Collectors.toList())))
+ .onFailure(th -> renderError(request, new JsonObject().put("error", th.getMessage())));
}
/**
- * Return list of accepted profile for discover visible
- * */
+ * Returns the list of profiles accepted by the discover-network feature for the authenticated user.
+ *
+ * Normandie only — this endpoint is part of the discover-network feature
+ * and is only active in the Normandie deployment.
+ *
+ * @param request the HTTP request
+ * @return {@code 200 OK} with a JSON array of profile name strings (e.g. {@code ["Teacher", "Student"]})
+ */
@Get("/discover/visible/profiles")
@SecuredAction(value= "", type = ActionType.AUTHENTICATED)
public void getDiscoverVisibleAcceptedProfile(HttpServerRequest request) {
- UserUtils.getUserInfos(eb, request, user -> {
- if(user == null) {
- badRequest(request, "invalid.user");
- return;
- }
- communicationService.getDiscoverVisibleAcceptedProfile(user, arrayResponseHandler(request));
- });
+ UserUtils.getAuthenticatedUserInfos(eb, request)
+ .compose(user -> communicationService.getDiscoverVisibleAcceptedProfile(user))
+ .onSuccess(results -> render(request, results.getList()))
+ .onFailure(th -> renderError(request, new JsonObject().put("error", th.getMessage())));
}
/**
- * Return list of all structures, to be use to filter discover visible users
- * */
+ * Returns the list of all structures available as filters for the discover-network feature.
+ *
+ * Normandie only — this endpoint is part of the discover-network feature
+ * and is only active in the Normandie deployment.
+ *
+ * @param request the HTTP request
+ * @return {@code 200 OK} with a JSON array of {@link DiscoverVisibleStructureDTO} entries
+ */
@Get("/discover/visible/structures")
@SecuredAction(value= "", type = ActionType.RESOURCE)
@ResourceFilter(CommunicationDiscoverVisibleFilter.class)
public void getDiscoverVisibleStructures(HttpServerRequest request) {
- UserUtils.getUserInfos(eb, request, user -> {
- if(user == null) {
- badRequest(request, "invalid.user");
- return;
- }
- communicationService.getDiscoverVisibleStructures(arrayResponseHandler(request));
- });
+ UserUtils.getAuthenticatedUserInfos(eb, request)
+ .compose(user -> communicationService.getDiscoverVisibleStructures())
+ .onSuccess(results -> render(request, results.stream()
+ .map(JsonObject.class::cast)
+ .map(DiscoverVisibleStructureDtoMapper::map)
+ .collect(Collectors.toList())))
+ .onFailure(th -> renderError(request, new JsonObject().put("error", th.getMessage())));
}
/**
- * Add communication rights between two users, this methode use "COMMUIQUE_DIRECT" with source 'MANUAL'
- * @param request
- * {
- * "receiverId": "receiverId"
- * }
- * */
+ * Creates a direct communication link ({@code COMMUNIQUE_DIRECT} with source {@code MANUAL})
+ * between the authenticated user and the specified receiver.
+ *
+ * Normandie only — this endpoint is part of the discover-network feature
+ * and is only active in the Normandie deployment.
+ *
+ * @param request the HTTP request; must contain a {@code receiverId} path parameter
+ * identifying the user to establish communication with
+ * @return {@code 200 OK} with a {@link CountResultDTO} body indicating the number of
+ * communication links created
+ */
@Post("/discover/visible/add/commuting/:receiverId")
@SecuredAction(value= "", type = ActionType.RESOURCE)
@ResourceFilter(CommunicationDiscoverVisibleFilter.class)
@@ -823,19 +968,24 @@ public void discoverVisibleAddCommuteUsers(HttpServerRequest request) {
badRequest(request, "invalid.parameter");
return;
}
-
- UserUtils.getUserInfos(eb, request, user -> {
- if(user == null) {
- badRequest(request, "invalid.user");
- return;
- }
- communicationService.discoverVisibleAddCommuteUsers(user, receiverId, request, notEmptyResponseHandler(request));
- });
+ UserUtils.getAuthenticatedUserInfos(eb, request)
+ .compose(user -> communicationService.discoverVisibleAddCommuteUsers(user, receiverId, request))
+ .onSuccess(result -> render(request, new CountResultDTO(result.getInteger("number"))))
+ .onFailure(th -> renderError(request, new JsonObject().put("error", th.getMessage())));
}
/**
- * Delete communication rights between two users, this methode delete "COMMUIQUE_DIRECT" with source 'MANUAL'
- * */
+ * Removes the direct communication link ({@code COMMUNIQUE_DIRECT} with source {@code MANUAL})
+ * between the authenticated user and the specified receiver.
+ *
+ * Normandie only — this endpoint is part of the discover-network feature
+ * and is only active in the Normandie deployment.
+ *
+ * @param request the HTTP request; must contain a {@code receiverId} path parameter
+ * identifying the user whose communication link should be removed
+ * @return {@code 200 OK} with a {@link CountResultDTO} body indicating the number of
+ * communication links removed
+ */
@Delete("/discover/visible/remove/commuting/:receiverId")
@SecuredAction(value= "", type = ActionType.RESOURCE)
@ResourceFilter(CommunicationDiscoverVisibleFilter.class)
@@ -845,39 +995,44 @@ public void discoverVisibleRemoveCommuteUsers(HttpServerRequest request) {
badRequest(request, "invalid.parameter");
return;
}
-
- UserUtils.getUserInfos(eb, request, user -> {
- if(user == null) {
- badRequest(request, "invalid.user");
- return;
- }
- communicationService.discoverVisibleRemoveCommuteUsers(user.getUserId(), receiverId, notEmptyResponseHandler(request));
- });
+ UserUtils.getAuthenticatedUserInfos(eb, request)
+ .compose(user -> communicationService.discoverVisibleRemoveCommuteUsers(user.getUserId(), receiverId))
+ .onSuccess(result -> render(request, new CountResultDTO(result.getInteger("number"))))
+ .onFailure(th -> renderError(request, new JsonObject().put("error", th.getMessage())));
}
/**
- * This methode return all groups that the user can see, with the group type 'manager'
- * */
+ * Returns all groups of type {@code manager} visible to the authenticated user.
+ *
+ * Normandie only — this endpoint is part of the discover-network feature
+ * and is only active in the Normandie deployment.
+ *
+ * @param request the HTTP request
+ * @return {@code 200 OK} with a JSON array of {@link GroupDTO} entries
+ */
@Get("/discover/visible/groups")
@SecuredAction(value= "", type = ActionType.RESOURCE)
@ResourceFilter(CommunicationDiscoverVisibleFilter.class)
public void discoverVisibleGetGroups(HttpServerRequest request) {
- UserUtils.getUserInfos(eb, request, user -> {
- if(user == null) {
- badRequest(request, "invalid.user");
- return;
- }
- communicationService.discoverVisibleGetGroups(user.getUserId(), arrayResponseHandler(request));
- });
+ UserUtils.getAuthenticatedUserInfos(eb, request)
+ .compose(user -> communicationService.discoverVisibleGetGroups(user.getUserId()))
+ .onSuccess(results -> render(request, results.stream()
+ .map(JsonObject.class::cast)
+ .map(GroupDtoMapper::map)
+ .collect(Collectors.toList())))
+ .onFailure(th -> renderError(request, new JsonObject().put("error", th.getMessage())));
}
/**
- * Get the list of users in a group
- * @param request
- * {
- * "groupId": "groupId"
- * }
- * */
+ * Returns the list of users belonging to a given discover-network group, filtered to those
+ * visible to the authenticated user.
+ *
+ * Normandie only — this endpoint is part of the discover-network feature
+ * and is only active in the Normandie deployment.
+ *
+ * @param request the HTTP request; must contain a {@code groupId} path parameter
+ * @return {@code 200 OK} with a JSON array of {@link UserDTO} entries
+ */
@Get("/discover/visible/group/:groupId/users")
@SecuredAction(value= "", type = ActionType.RESOURCE)
@ResourceFilter(CommunicationDiscoverVisibleFilter.class)
@@ -885,46 +1040,52 @@ public void discoverVisibleGetUsersInGroup(HttpServerRequest request) {
String groupId = request.params().get("groupId");
- UserUtils.getUserInfos(eb, request, user -> {
- if(user == null) {
- badRequest(request, "invalid.user");
- return;
- }
- communicationService.discoverVisibleGetUsersInGroup(user.getUserId(), groupId, arrayResponseHandler(request));
- });
+ UserUtils.getAuthenticatedUserInfos(eb, request)
+ .compose(user -> communicationService.discoverVisibleGetUsersInGroup(user.getUserId(), groupId))
+ .onSuccess(results -> render(request, results.stream()
+ .map(JsonObject.class::cast)
+ .map(UserDtoMapper::mapDiscoverVisibleGroupUser)
+ .collect(Collectors.toList())))
+ .onFailure(th -> renderError(request, new JsonObject().put("error", th.getMessage())));
}
/**
- * Create a group with the group type 'manager'
- * @param request
- * {
- * "name": "name",
- * }
- * */
+ * Creates a group of type {@code manager} for the discover-network feature.
+ *
+ * Normandie only — this endpoint is part of the discover-network feature
+ * and is only active in the Normandie deployment.
+ *
+ * @param request the HTTP request containing a {@link DiscoverVisibleGroupBodyDTO} JSON body
+ * @return {@code 200 OK} with an {@link IdentifiableDTO} body containing the id of the
+ * newly created group
+ */
@Post("/discover/visible/group")
@SecuredAction(value= "", type = ActionType.RESOURCE)
@ResourceFilter(CommunicationDiscoverVisibleFilter.class)
public void createDiscoverVisibleGroup(HttpServerRequest request) {
- RequestUtils.bodyToJson(request, body -> {
- UserUtils.getUserInfos(eb, request, user -> {
- if(user == null) {
- badRequest(request, "invalid.user");
- return;
- }
- communicationService.createDiscoverVisibleGroup(user.getUserId(), body, notEmptyResponseHandler(request));
- });
- });
+ RequestUtils.bodyToClass(request, DiscoverVisibleGroupBodyDTO.class).compose(body -> {
+ if (body.getName() == null || body.getName().trim().isEmpty()) {
+ return Future.failedFuture("invalid.name");
+ }
+ return UserUtils.getAuthenticatedUserInfos(eb, request)
+ .compose(user -> communicationService.createDiscoverVisibleGroup(user.getUserId(), body));
+ })
+ .onSuccess(result -> render(request, new IdentifiableDTO(result.getString("id"), null)))
+ .onFailure(th -> renderError(request, new JsonObject().put("error", th.getMessage())));
}
/**
- * Update a group with the group type 'manager'
- * @param request
- * {
- * "groupId": "groupId",
- * "name": "name",
- * }
- * */
+ * Updates the name of a discover-network group of type {@code manager}.
+ *
+ * Normandie only — this endpoint is part of the discover-network feature
+ * and is only active in the Normandie deployment.
+ *
+ * @param request the HTTP request; must contain a {@code groupId} path parameter and a
+ * {@link DiscoverVisibleGroupBodyDTO} JSON body with the new name
+ * @return {@code 200 OK} with an {@link IdentifiableDTO} body containing the id of the
+ * updated group
+ */
@Put("/discover/visible/group/:groupId")
@SecuredAction(value= "", type = ActionType.RESOURCE)
@ResourceFilter(CommunicationDiscoverVisibleFilter.class)
@@ -934,26 +1095,30 @@ public void updateDiscoverVisibleGroup(HttpServerRequest request) {
badRequest(request, "invalid.parameter");
return;
}
- RequestUtils.bodyToJson(request, body -> {
- UserUtils.getUserInfos(eb, request, user -> {
- if(user == null) {
- badRequest(request, "invalid.user");
- return;
- }
- communicationService.updateDiscoverVisibleGroup(user.getUserId(), groupId, body, notEmptyResponseHandler(request));
- });
- });
+ RequestUtils.bodyToClass(request, DiscoverVisibleGroupBodyDTO.class)
+ .compose(body -> {
+ if (body.getName() == null || body.getName().trim().isEmpty()) {
+ return Future.failedFuture("invalid.name");
+ }
+ return UserUtils.getAuthenticatedUserInfos(eb, request)
+ .compose(user -> communicationService.updateDiscoverVisibleGroup(user.getUserId(), groupId, body));
+ }).onSuccess(result -> render(request, new IdentifiableDTO(result.getString("id"), null)))
+ .onFailure(th -> renderError(request, new JsonObject().put("error", th.getMessage())));
}
/**
- * Update the groups member, with adding the new user in the group and send a notification to the user, and removing the user from the group
- * @param request
- * {
- * "groupId": "groupId",
- * "oldUsers": ["userId1", "userId2"], // list of users befor change
- * "newUsers": "["userId1", "userId2"]" //list of users after change
- * }
- * */
+ * Updates the members of a discover-network group by diffing the old and new user lists:
+ * users added to the group receive a notification, users removed from the group are silently
+ * unlinked.
+ *
+ * Normandie only — this endpoint is part of the discover-network feature
+ * and is only active in the Normandie deployment.
+ *
+ * @param request the HTTP request; must contain a {@code groupId} path parameter and a
+ * {@link DiscoverModifyGroupUsersDTO} JSON body with the previous and new
+ * member lists (max 100 users in {@code newUsers})
+ * @return {@code 200 OK} on success
+ */
@Put("/discover/visible/group/:groupId/users")
@SecuredAction(value= "", type = ActionType.RESOURCE)
@ResourceFilter(CommunicationDiscoverVisibleFilter.class)
@@ -964,15 +1129,18 @@ public void addDiscoverVisibleGroupUsers(HttpServerRequest request) {
return;
}
- RequestUtils.bodyToJson(request, body -> {
- UserUtils.getUserInfos(eb, request, user -> {
- if(user == null) {
- badRequest(request, "invalid.user");
- return;
- }
- communicationService.addDiscoverVisibleGroupUsers(user, groupId, body, request, defaultResponseHandler(request));
- });
- });
+ RequestUtils.bodyToClass(request, DiscoverModifyGroupUsersDTO.class).compose(body -> {
+ if (body.getNewUsers() == null || body.getNewUsers().isEmpty()) {
+ return Future.failedFuture("invalid.users");
+ }
+ if (body.getNewUsers().size() > 100) {
+ return Future.failedFuture("too.many.users");
+ }
+ return UserUtils.getAuthenticatedUserInfos(eb, request)
+ .compose(user -> communicationService.addDiscoverVisibleGroupUsers(user, groupId, body, request));
+ })
+ .onSuccess(v -> Renders.ok(request))
+ .onFailure(th -> renderError(request, new JsonObject().put("error", th.getMessage())));
}
/**
@@ -991,7 +1159,10 @@ public void searchVisibleContacts(HttpServerRequest request) {
final String mode = request.params().get("mode");
final boolean includeHidden = "true".equalsIgnoreCase(request.params().get("includeHidden"));
communicationService.searchVisibles(userInfos, query, mode, I18n.acceptLanguage(request), includeHidden)
- .onSuccess(visibles -> renderJson(request, visibles))
+ .onSuccess(visibles -> render(request, visibles.stream()
+ .map(JsonObject.class::cast)
+ .map(SearchVisibleContactDtoMapper::map)
+ .collect(Collectors.toList())))
.onFailure(th -> renderError(request, new JsonObject().put("error", th.getMessage())));
});
}
diff --git a/communication/src/main/java/org/entcore/communication/dto/bus/BusUserDTO.java b/communication/src/main/java/org/entcore/communication/dto/bus/BusUserDTO.java
new file mode 100644
index 0000000000..740c5abdc0
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/dto/bus/BusUserDTO.java
@@ -0,0 +1,72 @@
+package org.entcore.communication.dto.bus;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.vertx.codegen.annotations.DataObject;
+import io.vertx.codegen.json.annotations.JsonGen;
+import io.vertx.core.json.JsonObject;
+
+import java.util.List;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@DataObject
+@JsonGen
+public class BusUserDTO {
+
+ private String id;
+ private String name;
+ private String login;
+ private String username;
+ private String type;
+ private String profile;
+ private String lastName;
+ private String firstName;
+ private List profiles;
+ private List positionIds;
+ private List positionNames;
+
+ public BusUserDTO() {}
+
+ public BusUserDTO(JsonObject json) {
+ this();
+ BusUserDTOConverter.fromJson(json, this);
+ }
+
+ public JsonObject toJson() {
+ JsonObject json = new JsonObject();
+ BusUserDTOConverter.toJson(this, json);
+ return json;
+ }
+
+ public String getId() { return id; }
+ public BusUserDTO setId(String id) { this.id = id; return this; }
+
+ public String getName() { return name; }
+ public BusUserDTO setName(String name) { this.name = name; return this; }
+
+ public String getLogin() { return login; }
+ public BusUserDTO setLogin(String login) { this.login = login; return this; }
+
+ public String getUsername() { return username; }
+ public BusUserDTO setUsername(String username) { this.username = username; return this; }
+
+ public String getType() { return type; }
+ public BusUserDTO setType(String type) { this.type = type; return this; }
+
+ public String getProfile() { return profile; }
+ public BusUserDTO setProfile(String profile) { this.profile = profile; return this; }
+
+ public String getLastName() { return lastName; }
+ public BusUserDTO setLastName(String lastName) { this.lastName = lastName; return this; }
+
+ public String getFirstName() { return firstName; }
+ public BusUserDTO setFirstName(String firstName) { this.firstName = firstName; return this; }
+
+ public List getProfiles() { return profiles; }
+ public BusUserDTO setProfiles(List profiles) { this.profiles = profiles; return this; }
+
+ public List getPositionIds() { return positionIds; }
+ public BusUserDTO setPositionIds(List positionIds) { this.positionIds = positionIds; return this; }
+
+ public List getPositionNames() { return positionNames; }
+ public BusUserDTO setPositionNames(List positionNames) { this.positionNames = positionNames; return this; }
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/dto/bus/CommunicationBusDTO.java b/communication/src/main/java/org/entcore/communication/dto/bus/CommunicationBusDTO.java
new file mode 100644
index 0000000000..f38d22c09e
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/dto/bus/CommunicationBusDTO.java
@@ -0,0 +1,62 @@
+package org.entcore.communication.dto.bus;
+
+import io.vertx.codegen.annotations.DataObject;
+import io.vertx.codegen.json.annotations.JsonGen;
+import io.vertx.core.json.JsonObject;
+import java.util.List;
+
+@DataObject
+@JsonGen
+public class CommunicationBusDTO {
+
+ private String action;
+ private List schoolIds;
+ private String schoolId;
+ private Integer transactionId;
+ private Boolean commit;
+ private String groupId;
+ private String startGroupId;
+ private String endGroupId;
+ private String direction;
+
+ public CommunicationBusDTO() {}
+
+ public CommunicationBusDTO(JsonObject json) {
+ this();
+ CommunicationBusDTOConverter.fromJson(json, this);
+ }
+
+ public JsonObject toJson() {
+ JsonObject json = new JsonObject();
+ CommunicationBusDTOConverter.toJson(this, json);
+ return json;
+ }
+
+ public String getAction() { return action; }
+ public CommunicationBusDTO setAction(String action) { this.action = action; return this; }
+
+ public List getSchoolIds() { return schoolIds; }
+ public CommunicationBusDTO setSchoolIds(List schoolIds) { this.schoolIds = schoolIds; return this; }
+
+ public String getSchoolId() { return schoolId; }
+ public CommunicationBusDTO setSchoolId(String schoolId) { this.schoolId = schoolId; return this; }
+
+ public Integer getTransactionId() { return transactionId; }
+ public CommunicationBusDTO setTransactionId(Integer transactionId) { this.transactionId = transactionId; return this; }
+
+ /** Defaults to {@code true} when absent from the JSON payload. */
+ public boolean isCommit() { return commit == null || commit; }
+ public CommunicationBusDTO setCommit(boolean commit) { this.commit = commit; return this; }
+
+ public String getGroupId() { return groupId; }
+ public CommunicationBusDTO setGroupId(String groupId) { this.groupId = groupId; return this; }
+
+ public String getStartGroupId() { return startGroupId; }
+ public CommunicationBusDTO setStartGroupId(String startGroupId) { this.startGroupId = startGroupId; return this; }
+
+ public String getEndGroupId() { return endGroupId; }
+ public CommunicationBusDTO setEndGroupId(String endGroupId) { this.endGroupId = endGroupId; return this; }
+
+ public String getDirection() { return direction; }
+ public CommunicationBusDTO setDirection(String direction) { this.direction = direction; return this; }
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/dto/bus/SearchVisibleBusDTO.java b/communication/src/main/java/org/entcore/communication/dto/bus/SearchVisibleBusDTO.java
new file mode 100644
index 0000000000..d1b4e46cf4
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/dto/bus/SearchVisibleBusDTO.java
@@ -0,0 +1,88 @@
+package org.entcore.communication.dto.bus;
+
+import io.vertx.codegen.annotations.DataObject;
+import io.vertx.codegen.json.annotations.JsonGen;
+import io.vertx.core.json.JsonObject;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@DataObject
+@JsonGen
+public class SearchVisibleBusDTO {
+
+ private String userId;
+ private List userIds;
+ private String action;
+ private String schoolId;
+ private List expectedTypes = new ArrayList<>();
+ private String userProfile;
+ private String preFilter;
+ private String customReturn;
+ private boolean reverseUnion;
+ private Map additionnalParams;
+ // fields from the former SearchVisibleRequestDTO parent
+ private String search;
+ private boolean itSelf;
+ private boolean myGroup;
+ private boolean profile = true;
+
+ public SearchVisibleBusDTO() {}
+
+ public SearchVisibleBusDTO(JsonObject json) {
+ this();
+ SearchVisibleBusDTOConverter.fromJson(json, this);
+ }
+
+ public JsonObject toJson() {
+ JsonObject json = new JsonObject();
+ SearchVisibleBusDTOConverter.toJson(this, json);
+ return json;
+ }
+
+ public String getUserId() { return userId; }
+ public SearchVisibleBusDTO setUserId(String userId) { this.userId = userId; return this; }
+
+ public List getUserIds() { return userIds; }
+ public SearchVisibleBusDTO setUserIds(List userIds) { this.userIds = userIds; return this; }
+
+ public String getAction() { return action; }
+ public SearchVisibleBusDTO setAction(String action) { this.action = action; return this; }
+
+ public String getSchoolId() { return schoolId; }
+ public SearchVisibleBusDTO setSchoolId(String schoolId) { this.schoolId = schoolId; return this; }
+
+ public List getExpectedTypes() { return expectedTypes; }
+ public SearchVisibleBusDTO setExpectedTypes(List expectedTypes) {
+ this.expectedTypes = expectedTypes != null ? expectedTypes : new ArrayList<>();
+ return this;
+ }
+
+ public String getUserProfile() { return userProfile; }
+ public SearchVisibleBusDTO setUserProfile(String userProfile) { this.userProfile = userProfile; return this; }
+
+ public String getPreFilter() { return preFilter; }
+ public SearchVisibleBusDTO setPreFilter(String preFilter) { this.preFilter = preFilter; return this; }
+
+ public String getCustomReturn() { return customReturn; }
+ public SearchVisibleBusDTO setCustomReturn(String customReturn) { this.customReturn = customReturn; return this; }
+
+ public boolean isReverseUnion() { return reverseUnion; }
+ public SearchVisibleBusDTO setReverseUnion(boolean reverseUnion) { this.reverseUnion = reverseUnion; return this; }
+
+ public Map getAdditionnalParams() { return additionnalParams; }
+ public SearchVisibleBusDTO setAdditionnalParams(Map additionnalParams) { this.additionnalParams = additionnalParams; return this; }
+
+ public String getSearch() { return search; }
+ public SearchVisibleBusDTO setSearch(String search) { this.search = search; return this; }
+
+ public boolean isItSelf() { return itSelf; }
+ public SearchVisibleBusDTO setItSelf(boolean itSelf) { this.itSelf = itSelf; return this; }
+
+ public boolean isMyGroup() { return myGroup; }
+ public SearchVisibleBusDTO setMyGroup(boolean myGroup) { this.myGroup = myGroup; return this; }
+
+ /** Defaults to {@code true} when absent from the JSON payload. */
+ public boolean isProfile() { return profile; }
+ public SearchVisibleBusDTO setProfile(boolean profile) { this.profile = profile; return this; }
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/dto/bus/package-info.java b/communication/src/main/java/org/entcore/communication/dto/bus/package-info.java
new file mode 100644
index 0000000000..d2cbd0612c
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/dto/bus/package-info.java
@@ -0,0 +1,4 @@
+@ModuleGen(name = "communication-dto-bus", groupPackage = "org.entcore.communication")
+package org.entcore.communication.dto.bus;
+
+import io.vertx.codegen.annotations.ModuleGen;
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/dto/rest/AddLinkDirectionsDTO.java b/communication/src/main/java/org/entcore/communication/dto/rest/AddLinkDirectionsDTO.java
new file mode 100644
index 0000000000..93940f32f3
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/dto/rest/AddLinkDirectionsDTO.java
@@ -0,0 +1,18 @@
+package org.entcore.communication.dto.rest;
+
+import org.entcore.communication.services.CommunicationService;
+
+import java.util.Map;
+
+public class AddLinkDirectionsDTO {
+
+ private final Map directions;
+
+ public AddLinkDirectionsDTO(Map directions) {
+ this.directions = directions;
+ }
+
+ public Map getDirections() {
+ return directions;
+ }
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/dto/rest/CommuniqueWithDTO.java b/communication/src/main/java/org/entcore/communication/dto/rest/CommuniqueWithDTO.java
new file mode 100644
index 0000000000..337df766a7
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/dto/rest/CommuniqueWithDTO.java
@@ -0,0 +1,38 @@
+package org.entcore.communication.dto.rest;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.vertx.codegen.annotations.DataObject;
+import io.vertx.codegen.json.annotations.JsonGen;
+import io.vertx.core.json.JsonObject;
+
+import java.util.List;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@DataObject
+@JsonGen(inheritConverter = true)
+public class CommuniqueWithDTO extends GroupDTO {
+
+ private List communiqueWith;
+
+ public CommuniqueWithDTO() {}
+
+ public CommuniqueWithDTO(JsonObject json) {
+ this();
+ CommuniqueWithDTOConverter.fromJson(json, this);
+ }
+
+ public JsonObject toJson() {
+ JsonObject json = new JsonObject();
+ CommuniqueWithDTOConverter.toJson(this, json);
+ return json;
+ }
+
+ public List getCommuniqueWith() {
+ return communiqueWith;
+ }
+
+ public CommuniqueWithDTO setCommuniqueWith(List communiqueWith) {
+ this.communiqueWith = communiqueWith;
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/dto/rest/ContactRefDTO.java b/communication/src/main/java/org/entcore/communication/dto/rest/ContactRefDTO.java
new file mode 100644
index 0000000000..49ff195135
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/dto/rest/ContactRefDTO.java
@@ -0,0 +1,46 @@
+package org.entcore.communication.dto.rest;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.vertx.codegen.annotations.DataObject;
+import io.vertx.codegen.json.annotations.JsonGen;
+import io.vertx.core.json.JsonObject;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@DataObject
+@JsonGen
+public class ContactRefDTO {
+
+ private String id;
+ private String displayName;
+
+ public ContactRefDTO() {}
+
+ public ContactRefDTO(JsonObject json) {
+ this();
+ ContactRefDTOConverter.fromJson(json, this);
+ }
+
+ public JsonObject toJson() {
+ JsonObject json = new JsonObject();
+ ContactRefDTOConverter.toJson(this, json);
+ return json;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public ContactRefDTO setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public ContactRefDTO setDisplayName(String displayName) {
+ this.displayName = displayName;
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/dto/rest/CountResultDTO.java b/communication/src/main/java/org/entcore/communication/dto/rest/CountResultDTO.java
new file mode 100644
index 0000000000..efa7e9a97f
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/dto/rest/CountResultDTO.java
@@ -0,0 +1,14 @@
+package org.entcore.communication.dto.rest;
+
+public class CountResultDTO {
+
+ private final int count;
+
+ public CountResultDTO(int count) {
+ this.count = count;
+ }
+
+ public int getCount() {
+ return count;
+ }
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/dto/rest/DiscoverModifyGroupUsersDTO.java b/communication/src/main/java/org/entcore/communication/dto/rest/DiscoverModifyGroupUsersDTO.java
new file mode 100644
index 0000000000..c79f5602f4
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/dto/rest/DiscoverModifyGroupUsersDTO.java
@@ -0,0 +1,48 @@
+package org.entcore.communication.dto.rest;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import io.vertx.codegen.annotations.DataObject;
+import io.vertx.codegen.json.annotations.JsonGen;
+import io.vertx.core.json.JsonObject;
+import java.util.ArrayList;
+import java.util.List;
+
+@DataObject
+@JsonGen
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class DiscoverModifyGroupUsersDTO {
+
+ private List oldUsers = new ArrayList<>();
+ private List newUsers;
+
+ public DiscoverModifyGroupUsersDTO() {}
+
+ public DiscoverModifyGroupUsersDTO(JsonObject json) {
+ this();
+ DiscoverModifyGroupUsersDTOConverter.fromJson(json, this);
+ }
+
+ public JsonObject toJson() {
+ JsonObject json = new JsonObject();
+ DiscoverModifyGroupUsersDTOConverter.toJson(this, json);
+ return json;
+ }
+
+ public List getOldUsers() {
+ return oldUsers;
+ }
+
+ public DiscoverModifyGroupUsersDTO setOldUsers(List oldUsers) {
+ this.oldUsers = oldUsers != null ? oldUsers : new ArrayList<>();
+ return this;
+ }
+
+ public List getNewUsers() {
+ return newUsers;
+ }
+
+ public DiscoverModifyGroupUsersDTO setNewUsers(List newUsers) {
+ this.newUsers = newUsers;
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/dto/rest/DiscoverVisibleFilterDTO.java b/communication/src/main/java/org/entcore/communication/dto/rest/DiscoverVisibleFilterDTO.java
new file mode 100644
index 0000000000..e5df403e0b
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/dto/rest/DiscoverVisibleFilterDTO.java
@@ -0,0 +1,57 @@
+package org.entcore.communication.dto.rest;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import io.vertx.codegen.annotations.DataObject;
+import io.vertx.codegen.json.annotations.JsonGen;
+import io.vertx.core.json.JsonObject;
+import java.util.List;
+
+@DataObject
+@JsonGen
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class DiscoverVisibleFilterDTO {
+
+ private List structures;
+ private List profiles;
+ private String search;
+
+ public DiscoverVisibleFilterDTO() {}
+
+ public DiscoverVisibleFilterDTO(JsonObject json) {
+ this();
+ DiscoverVisibleFilterDTOConverter.fromJson(json, this);
+ }
+
+ public JsonObject toJson() {
+ JsonObject json = new JsonObject();
+ DiscoverVisibleFilterDTOConverter.toJson(this, json);
+ return json;
+ }
+
+ public List getStructures() {
+ return structures;
+ }
+
+ public DiscoverVisibleFilterDTO setStructures(List structures) {
+ this.structures = structures;
+ return this;
+ }
+
+ public List getProfiles() {
+ return profiles;
+ }
+
+ public DiscoverVisibleFilterDTO setProfiles(List profiles) {
+ this.profiles = profiles;
+ return this;
+ }
+
+ public String getSearch() {
+ return search;
+ }
+
+ public DiscoverVisibleFilterDTO setSearch(String search) {
+ this.search = search;
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/dto/rest/DiscoverVisibleGroupBodyDTO.java b/communication/src/main/java/org/entcore/communication/dto/rest/DiscoverVisibleGroupBodyDTO.java
new file mode 100644
index 0000000000..752d8441b0
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/dto/rest/DiscoverVisibleGroupBodyDTO.java
@@ -0,0 +1,36 @@
+package org.entcore.communication.dto.rest;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import io.vertx.codegen.annotations.DataObject;
+import io.vertx.codegen.json.annotations.JsonGen;
+import io.vertx.core.json.JsonObject;
+
+@DataObject
+@JsonGen
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class DiscoverVisibleGroupBodyDTO {
+
+ private String name;
+
+ public DiscoverVisibleGroupBodyDTO() {}
+
+ public DiscoverVisibleGroupBodyDTO(JsonObject json) {
+ this();
+ DiscoverVisibleGroupBodyDTOConverter.fromJson(json, this);
+ }
+
+ public JsonObject toJson() {
+ JsonObject json = new JsonObject();
+ DiscoverVisibleGroupBodyDTOConverter.toJson(this, json);
+ return json;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public DiscoverVisibleGroupBodyDTO setName(String name) {
+ this.name = name;
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/dto/rest/DiscoverVisibleStructureDTO.java b/communication/src/main/java/org/entcore/communication/dto/rest/DiscoverVisibleStructureDTO.java
new file mode 100644
index 0000000000..9d45c50ac6
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/dto/rest/DiscoverVisibleStructureDTO.java
@@ -0,0 +1,24 @@
+package org.entcore.communication.dto.rest;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class DiscoverVisibleStructureDTO {
+
+ private final String id;
+ private final String type;
+ private final String label;
+ private final boolean checked;
+
+ public DiscoverVisibleStructureDTO(String id, String type, String label, boolean checked) {
+ this.id = id;
+ this.type = type;
+ this.label = label;
+ this.checked = checked;
+ }
+
+ public String getId() { return id; }
+ public String getType() { return type; }
+ public String getLabel() { return label; }
+ public boolean isChecked() { return checked; }
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/dto/rest/GroupDTO.java b/communication/src/main/java/org/entcore/communication/dto/rest/GroupDTO.java
new file mode 100644
index 0000000000..a86914160e
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/dto/rest/GroupDTO.java
@@ -0,0 +1,156 @@
+package org.entcore.communication.dto.rest;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.vertx.codegen.annotations.DataObject;
+import io.vertx.codegen.json.annotations.JsonGen;
+import io.vertx.core.json.JsonObject;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@DataObject
+@JsonGen(inheritConverter = true)
+public class GroupDTO extends IdentifiableDTO {
+
+ private String filter;
+ private String profile;
+ private String displayName;
+ private String groupDisplayName;
+ private InternalCommunicationRule internalCommunicationRule;
+ private List classes = new ArrayList<>();
+ private List structures = new ArrayList<>();
+ private String type;
+ private String groupType;
+ private String subType;
+ private String sortName;
+ private Integer nbUsers;
+
+ public GroupDTO() {}
+
+ public GroupDTO(JsonObject json) {
+ this();
+ GroupDTOConverter.fromJson(json, this);
+ }
+
+ public JsonObject toJson() {
+ JsonObject json = new JsonObject();
+ GroupDTOConverter.toJson(this, json);
+ return json;
+ }
+
+ public String getFilter() {
+ return filter;
+ }
+
+ public GroupDTO setFilter(String filter) {
+ this.filter = filter;
+ return this;
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public GroupDTO setDisplayName(String displayName) {
+ this.displayName = displayName;
+ return this;
+ }
+
+ public InternalCommunicationRule getInternalCommunicationRule() {
+ return internalCommunicationRule;
+ }
+
+ public GroupDTO setInternalCommunicationRule(InternalCommunicationRule internalCommunicationRule) {
+ this.internalCommunicationRule = internalCommunicationRule;
+ return this;
+ }
+
+ public List getClasses() {
+ return classes;
+ }
+
+ public GroupDTO setClasses(List classes) {
+ this.classes = classes;
+ return this;
+ }
+
+ public List getStructures() {
+ return structures;
+ }
+
+ public GroupDTO setStructures(List structures) {
+ this.structures = structures;
+ return this;
+ }
+
+ public String getSubType() {
+ return subType;
+ }
+
+ public GroupDTO setSubType(String subType) {
+ this.subType = subType;
+ return this;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public GroupDTO setType(String type) {
+ this.type = type;
+ return this;
+ }
+
+ public String getProfile() {
+ return profile;
+ }
+
+ public GroupDTO setProfile(String profile) {
+ this.profile = profile;
+ return this;
+ }
+
+ public String getGroupType() {
+ return groupType;
+ }
+
+ public GroupDTO setGroupType(String groupType) {
+ this.groupType = groupType;
+ return this;
+ }
+
+ public String getSortName() {
+ return sortName;
+ }
+
+ public GroupDTO setSortName(String sortName) {
+ this.sortName = sortName;
+ return this;
+ }
+
+ public Integer getNbUsers() {
+ return nbUsers;
+ }
+
+ public GroupDTO setNbUsers(Integer nbUsers) {
+ this.nbUsers = nbUsers;
+ return this;
+ }
+
+ public String getGroupDisplayName() {
+ return groupDisplayName;
+ }
+
+ public GroupDTO setGroupDisplayName(String groupDisplayName) {
+ this.groupDisplayName = groupDisplayName;
+ return this;
+ }
+
+ public enum InternalCommunicationRule {
+ NONE,
+ INCOMING,
+ OUTGOING,
+ BOTH;
+ }
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/dto/rest/GroupUsersDirectionDTO.java b/communication/src/main/java/org/entcore/communication/dto/rest/GroupUsersDirectionDTO.java
new file mode 100644
index 0000000000..9050a89028
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/dto/rest/GroupUsersDirectionDTO.java
@@ -0,0 +1,16 @@
+package org.entcore.communication.dto.rest;
+
+import org.entcore.communication.services.CommunicationService;
+
+public class GroupUsersDirectionDTO {
+
+ private final CommunicationService.Direction users;
+
+ public GroupUsersDirectionDTO(CommunicationService.Direction users) {
+ this.users = users;
+ }
+
+ public CommunicationService.Direction getUsers() {
+ return users;
+ }
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/dto/rest/IdentifiableDTO.java b/communication/src/main/java/org/entcore/communication/dto/rest/IdentifiableDTO.java
new file mode 100644
index 0000000000..1c577e2d21
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/dto/rest/IdentifiableDTO.java
@@ -0,0 +1,51 @@
+package org.entcore.communication.dto.rest;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.vertx.codegen.annotations.DataObject;
+import io.vertx.codegen.json.annotations.JsonGen;
+import io.vertx.core.json.JsonObject;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@DataObject
+@JsonGen
+public class IdentifiableDTO {
+
+ private String id;
+ private String name;
+
+ public IdentifiableDTO() {}
+
+ public IdentifiableDTO(String id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public IdentifiableDTO(JsonObject json) {
+ this();
+ IdentifiableDTOConverter.fromJson(json, this);
+ }
+
+ public JsonObject toJson() {
+ JsonObject json = new JsonObject();
+ IdentifiableDTOConverter.toJson(this, json);
+ return json;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public IdentifiableDTO setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public IdentifiableDTO setName(String name) {
+ this.name = name;
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/dto/rest/InitDefaultRulesDTO.java b/communication/src/main/java/org/entcore/communication/dto/rest/InitDefaultRulesDTO.java
new file mode 100644
index 0000000000..4f349742e9
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/dto/rest/InitDefaultRulesDTO.java
@@ -0,0 +1,35 @@
+package org.entcore.communication.dto.rest;
+
+import io.vertx.codegen.annotations.DataObject;
+import io.vertx.codegen.json.annotations.JsonGen;
+import io.vertx.core.json.JsonObject;
+import java.util.List;
+
+@DataObject
+@JsonGen
+public class InitDefaultRulesDTO {
+
+ private List structures;
+
+ public InitDefaultRulesDTO() {}
+
+ public InitDefaultRulesDTO(JsonObject json) {
+ this();
+ InitDefaultRulesDTOConverter.fromJson(json, this);
+ }
+
+ public JsonObject toJson() {
+ JsonObject json = new JsonObject();
+ InitDefaultRulesDTOConverter.toJson(this, json);
+ return json;
+ }
+
+ public List getStructures() {
+ return structures;
+ }
+
+ public InitDefaultRulesDTO setStructures(List structures) {
+ this.structures = structures;
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/dto/rest/RemoveRelationsResultDTO.java b/communication/src/main/java/org/entcore/communication/dto/rest/RemoveRelationsResultDTO.java
new file mode 100644
index 0000000000..00077a18bd
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/dto/rest/RemoveRelationsResultDTO.java
@@ -0,0 +1,22 @@
+package org.entcore.communication.dto.rest;
+
+import org.entcore.communication.services.CommunicationService;
+
+public class RemoveRelationsResultDTO {
+
+ private final CommunicationService.Direction sender;
+ private final CommunicationService.Direction receiver;
+
+ public RemoveRelationsResultDTO(CommunicationService.Direction sender, CommunicationService.Direction receiver) {
+ this.sender = sender;
+ this.receiver = receiver;
+ }
+
+ public CommunicationService.Direction getSender() {
+ return sender;
+ }
+
+ public CommunicationService.Direction getReceiver() {
+ return receiver;
+ }
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/dto/rest/SearchVisibleContactDTO.java b/communication/src/main/java/org/entcore/communication/dto/rest/SearchVisibleContactDTO.java
new file mode 100644
index 0000000000..ed86a0a9b3
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/dto/rest/SearchVisibleContactDTO.java
@@ -0,0 +1,134 @@
+package org.entcore.communication.dto.rest;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.vertx.codegen.annotations.DataObject;
+import io.vertx.codegen.json.annotations.JsonGen;
+import io.vertx.core.json.JsonObject;
+
+import java.util.List;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@DataObject
+@JsonGen
+public class SearchVisibleContactDTO {
+
+ private String id;
+ private String displayName;
+ private String type;
+ private List usedIn;
+
+ // users and ProfileGroup
+ private String profile;
+
+ // groups
+ private String groupType;
+ private Integer nbUsers;
+ private String structureName;
+
+ // non-student users
+ private List children;
+ private List relatives;
+
+ public SearchVisibleContactDTO() {}
+
+ public SearchVisibleContactDTO(JsonObject json) {
+ this();
+ SearchVisibleContactDTOConverter.fromJson(json, this);
+ }
+
+ public JsonObject toJson() {
+ JsonObject json = new JsonObject();
+ SearchVisibleContactDTOConverter.toJson(this, json);
+ return json;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public SearchVisibleContactDTO setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public SearchVisibleContactDTO setDisplayName(String displayName) {
+ this.displayName = displayName;
+ return this;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public SearchVisibleContactDTO setType(String type) {
+ this.type = type;
+ return this;
+ }
+
+ public List getUsedIn() {
+ return usedIn;
+ }
+
+ public SearchVisibleContactDTO setUsedIn(List usedIn) {
+ this.usedIn = usedIn;
+ return this;
+ }
+
+ public String getProfile() {
+ return profile;
+ }
+
+ public SearchVisibleContactDTO setProfile(String profile) {
+ this.profile = profile;
+ return this;
+ }
+
+ public String getGroupType() {
+ return groupType;
+ }
+
+ public SearchVisibleContactDTO setGroupType(String groupType) {
+ this.groupType = groupType;
+ return this;
+ }
+
+ public Integer getNbUsers() {
+ return nbUsers;
+ }
+
+ public SearchVisibleContactDTO setNbUsers(Integer nbUsers) {
+ this.nbUsers = nbUsers;
+ return this;
+ }
+
+ public String getStructureName() {
+ return structureName;
+ }
+
+ public SearchVisibleContactDTO setStructureName(String structureName) {
+ this.structureName = structureName;
+ return this;
+ }
+
+ public List getChildren() {
+ return children;
+ }
+
+ public SearchVisibleContactDTO setChildren(List children) {
+ this.children = children;
+ return this;
+ }
+
+ public List getRelatives() {
+ return relatives;
+ }
+
+ public SearchVisibleContactDTO setRelatives(List relatives) {
+ this.relatives = relatives;
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/dto/rest/SearchVisibleRequestDTO.java b/communication/src/main/java/org/entcore/communication/dto/rest/SearchVisibleRequestDTO.java
new file mode 100644
index 0000000000..b922844653
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/dto/rest/SearchVisibleRequestDTO.java
@@ -0,0 +1,84 @@
+package org.entcore.communication.dto.rest;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import io.vertx.codegen.annotations.DataObject;
+import io.vertx.codegen.json.annotations.JsonGen;
+import io.vertx.core.json.JsonObject;
+import java.util.List;
+
+@DataObject
+@JsonGen
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class SearchVisibleRequestDTO {
+
+ private List structures;
+ private List classes;
+ private List profiles;
+ private List functions;
+ private List positions;
+ private String search;
+ private List types;
+ private Boolean nbUsersInGroups;
+ private Boolean groupType;
+ private boolean itSelf;
+ private boolean myGroup;
+ private boolean profile = true;
+
+ public SearchVisibleRequestDTO() {}
+
+ public SearchVisibleRequestDTO(JsonObject json) {
+ this();
+ SearchVisibleRequestDTOConverter.fromJson(json, this);
+ }
+
+ public JsonObject toJson() {
+ JsonObject json = new JsonObject();
+ SearchVisibleRequestDTOConverter.toJson(this, json);
+ return json;
+ }
+
+ /**
+ * Returns this instance after applying server-side overrides for the REST visible endpoint:
+ * {@code itSelf=true}, {@code myGroup=true}, {@code profile=false}.
+ */
+ public SearchVisibleRequestDTO withServerDefaults() {
+ return setItSelf(true).setMyGroup(true).setProfile(false);
+ }
+
+ public List getStructures() { return structures; }
+ public SearchVisibleRequestDTO setStructures(List structures) { this.structures = structures; return this; }
+
+ public List getClasses() { return classes; }
+ public SearchVisibleRequestDTO setClasses(List classes) { this.classes = classes; return this; }
+
+ public List getProfiles() { return profiles; }
+ public SearchVisibleRequestDTO setProfiles(List profiles) { this.profiles = profiles; return this; }
+
+ public List getFunctions() { return functions; }
+ public SearchVisibleRequestDTO setFunctions(List functions) { this.functions = functions; return this; }
+
+ public List getPositions() { return positions; }
+ public SearchVisibleRequestDTO setPositions(List positions) { this.positions = positions; return this; }
+
+ public String getSearch() { return search; }
+ public SearchVisibleRequestDTO setSearch(String search) { this.search = search; return this; }
+
+ public List getTypes() { return types; }
+ public SearchVisibleRequestDTO setTypes(List types) { this.types = types; return this; }
+
+ public Boolean getNbUsersInGroups() { return nbUsersInGroups; }
+ public SearchVisibleRequestDTO setNbUsersInGroups(Boolean nbUsersInGroups) { this.nbUsersInGroups = nbUsersInGroups; return this; }
+
+ public Boolean getGroupType() { return groupType; }
+ public SearchVisibleRequestDTO setGroupType(Boolean groupType) { this.groupType = groupType; return this; }
+
+ public boolean isItSelf() { return itSelf; }
+ public SearchVisibleRequestDTO setItSelf(boolean itSelf) { this.itSelf = itSelf; return this; }
+
+ public boolean isMyGroup() { return myGroup; }
+ public SearchVisibleRequestDTO setMyGroup(boolean myGroup) { this.myGroup = myGroup; return this; }
+
+ /** Defaults to {@code true} when absent from the JSON payload. */
+ public boolean isProfile() { return profile; }
+ public SearchVisibleRequestDTO setProfile(boolean profile) { this.profile = profile; return this; }
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/dto/rest/SearchVisibleResultDTO.java b/communication/src/main/java/org/entcore/communication/dto/rest/SearchVisibleResultDTO.java
new file mode 100644
index 0000000000..b4670b5fae
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/dto/rest/SearchVisibleResultDTO.java
@@ -0,0 +1,22 @@
+package org.entcore.communication.dto.rest;
+
+import java.util.List;
+
+public class SearchVisibleResultDTO {
+
+ private final List groups;
+ private final List users;
+
+ public SearchVisibleResultDTO(List groups, List users) {
+ this.groups = groups;
+ this.users = users;
+ }
+
+ public List getGroups() {
+ return groups;
+ }
+
+ public List getUsers() {
+ return users;
+ }
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/dto/rest/UserDTO.java b/communication/src/main/java/org/entcore/communication/dto/rest/UserDTO.java
new file mode 100644
index 0000000000..d3154e92c7
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/dto/rest/UserDTO.java
@@ -0,0 +1,100 @@
+package org.entcore.communication.dto.rest;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+import java.util.List;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class UserDTO extends IdentifiableDTO {
+
+ private String displayName;
+ private String groupDisplayName;
+ private String profile;
+ private String type;
+ private String login;
+ private List subjects;
+ private List positions;
+ private List structures;
+ private Boolean hasCommunication;
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public UserDTO setDisplayName(String displayName) {
+ this.displayName = displayName;
+ return this;
+ }
+
+ public String getGroupDisplayName() {
+ return groupDisplayName;
+ }
+
+ public UserDTO setGroupDisplayName(String groupDisplayName) {
+ this.groupDisplayName = groupDisplayName;
+ return this;
+ }
+
+ public String getProfile() {
+ return profile;
+ }
+
+ public UserDTO setProfile(String profile) {
+ this.profile = profile;
+ return this;
+ }
+
+ public List getSubjects() {
+ return subjects;
+ }
+
+ public UserDTO setSubjects(List subjects) {
+ this.subjects = subjects;
+ return this;
+ }
+
+ public List getPositions() {
+ return positions;
+ }
+
+ public UserDTO setPositions(List positions) {
+ this.positions = positions;
+ return this;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public UserDTO setType(String type) {
+ this.type = type;
+ return this;
+ }
+
+ public String getLogin() {
+ return login;
+ }
+
+ public UserDTO setLogin(String login) {
+ this.login = login;
+ return this;
+ }
+
+ public List getStructures() {
+ return structures;
+ }
+
+ public UserDTO setStructures(List structures) {
+ this.structures = structures;
+ return this;
+ }
+
+ public Boolean getHasCommunication() {
+ return hasCommunication;
+ }
+
+ public UserDTO setHasCommunication(Boolean hasCommunication) {
+ this.hasCommunication = hasCommunication;
+ return this;
+ }
+}
diff --git a/communication/src/main/java/org/entcore/communication/dto/rest/VerifyResultDTO.java b/communication/src/main/java/org/entcore/communication/dto/rest/VerifyResultDTO.java
new file mode 100644
index 0000000000..5cbd7dd0fb
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/dto/rest/VerifyResultDTO.java
@@ -0,0 +1,38 @@
+package org.entcore.communication.dto.rest;
+
+import io.vertx.codegen.annotations.DataObject;
+import io.vertx.codegen.json.annotations.JsonGen;
+import io.vertx.core.json.JsonObject;
+
+@DataObject
+@JsonGen
+public class VerifyResultDTO {
+
+ private boolean canCommunicate;
+
+ public VerifyResultDTO() {}
+
+ public VerifyResultDTO(boolean canCommunicate) {
+ this.canCommunicate = canCommunicate;
+ }
+
+ public VerifyResultDTO(JsonObject json) {
+ this();
+ VerifyResultDTOConverter.fromJson(json, this);
+ }
+
+ public JsonObject toJson() {
+ JsonObject json = new JsonObject();
+ VerifyResultDTOConverter.toJson(this, json);
+ return json;
+ }
+
+ public boolean isCanCommunicate() {
+ return canCommunicate;
+ }
+
+ public VerifyResultDTO setCanCommunicate(boolean canCommunicate) {
+ this.canCommunicate = canCommunicate;
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/dto/rest/package-info.java b/communication/src/main/java/org/entcore/communication/dto/rest/package-info.java
new file mode 100644
index 0000000000..b6bac9a276
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/dto/rest/package-info.java
@@ -0,0 +1,4 @@
+@ModuleGen(name = "communication-dto-rest", groupPackage = "org.entcore.communication")
+package org.entcore.communication.dto.rest;
+
+import io.vertx.codegen.annotations.ModuleGen;
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/mapper/AddLinkDirectionsDtoMapper.java b/communication/src/main/java/org/entcore/communication/mapper/AddLinkDirectionsDtoMapper.java
new file mode 100644
index 0000000000..e47d3304ad
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/mapper/AddLinkDirectionsDtoMapper.java
@@ -0,0 +1,20 @@
+package org.entcore.communication.mapper;
+
+import io.vertx.core.json.JsonObject;
+import org.entcore.communication.dto.rest.AddLinkDirectionsDTO;
+import org.entcore.communication.services.CommunicationService;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class AddLinkDirectionsDtoMapper {
+
+ public static AddLinkDirectionsDTO map(JsonObject json) {
+ Map directions = new HashMap<>();
+ if (!json.containsKey("ok")) {
+ json.forEach(entry -> directions.put(entry.getKey(),
+ CommunicationService.Direction.fromString(entry.getValue().toString())));
+ }
+ return new AddLinkDirectionsDTO(directions);
+ }
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/mapper/BusUserDtoMapper.java b/communication/src/main/java/org/entcore/communication/mapper/BusUserDtoMapper.java
new file mode 100644
index 0000000000..71e14ff617
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/mapper/BusUserDtoMapper.java
@@ -0,0 +1,11 @@
+package org.entcore.communication.mapper;
+
+import io.vertx.core.json.JsonObject;
+import org.entcore.communication.dto.bus.BusUserDTO;
+
+public class BusUserDtoMapper {
+
+ public static BusUserDTO map(JsonObject json) {
+ return new BusUserDTO(json);
+ }
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/mapper/CommuniqueWithDtoMapper.java b/communication/src/main/java/org/entcore/communication/mapper/CommuniqueWithDtoMapper.java
new file mode 100644
index 0000000000..dc754ed9aa
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/mapper/CommuniqueWithDtoMapper.java
@@ -0,0 +1,11 @@
+package org.entcore.communication.mapper;
+
+import io.vertx.core.json.JsonObject;
+import org.entcore.communication.dto.rest.CommuniqueWithDTO;
+
+public class CommuniqueWithDtoMapper {
+
+ public static CommuniqueWithDTO map(JsonObject json) {
+ return json == null ? null : new CommuniqueWithDTO(json);
+ }
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/mapper/DiscoverVisibleStructureDtoMapper.java b/communication/src/main/java/org/entcore/communication/mapper/DiscoverVisibleStructureDtoMapper.java
new file mode 100644
index 0000000000..c6066ede3b
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/mapper/DiscoverVisibleStructureDtoMapper.java
@@ -0,0 +1,16 @@
+package org.entcore.communication.mapper;
+
+import io.vertx.core.json.JsonObject;
+import org.entcore.communication.dto.rest.DiscoverVisibleStructureDTO;
+
+public class DiscoverVisibleStructureDtoMapper {
+
+ public static DiscoverVisibleStructureDTO map(JsonObject json) {
+ return new DiscoverVisibleStructureDTO(
+ json.getString("id"),
+ json.getString("type"),
+ json.getString("label"),
+ Boolean.parseBoolean(json.getString("checked"))
+ );
+ }
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/mapper/GroupDtoMapper.java b/communication/src/main/java/org/entcore/communication/mapper/GroupDtoMapper.java
new file mode 100644
index 0000000000..a95150970f
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/mapper/GroupDtoMapper.java
@@ -0,0 +1,12 @@
+package org.entcore.communication.mapper;
+
+import io.vertx.core.json.JsonObject;
+import org.entcore.communication.dto.rest.GroupDTO;
+
+public class GroupDtoMapper {
+
+ public static GroupDTO map(JsonObject json) {
+ return json == null ? null : new GroupDTO(json);
+ }
+
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/mapper/IdentifiableDtoMapper.java b/communication/src/main/java/org/entcore/communication/mapper/IdentifiableDtoMapper.java
new file mode 100644
index 0000000000..72ea47592c
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/mapper/IdentifiableDtoMapper.java
@@ -0,0 +1,12 @@
+package org.entcore.communication.mapper;
+
+import io.vertx.core.json.JsonObject;
+import org.entcore.communication.dto.rest.IdentifiableDTO;
+
+public class IdentifiableDtoMapper {
+
+ public static IdentifiableDTO map(JsonObject json) {
+ return new IdentifiableDTO(json);
+ }
+
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/mapper/RemoveRelationsResultDtoMapper.java b/communication/src/main/java/org/entcore/communication/mapper/RemoveRelationsResultDtoMapper.java
new file mode 100644
index 0000000000..a1fda91255
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/mapper/RemoveRelationsResultDtoMapper.java
@@ -0,0 +1,17 @@
+package org.entcore.communication.mapper;
+
+import io.vertx.core.json.JsonObject;
+import org.entcore.communication.dto.rest.RemoveRelationsResultDTO;
+import org.entcore.communication.services.CommunicationService;
+
+public class RemoveRelationsResultDtoMapper {
+
+ public static RemoveRelationsResultDTO map(JsonObject json) {
+ String sender = json.getString("sender");
+ String receiver = json.getString("receiver");
+ return new RemoveRelationsResultDTO(
+ sender != null ? CommunicationService.Direction.fromString(sender) : null,
+ receiver != null ? CommunicationService.Direction.fromString(receiver) : null
+ );
+ }
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/mapper/SearchVisibleContactDtoMapper.java b/communication/src/main/java/org/entcore/communication/mapper/SearchVisibleContactDtoMapper.java
new file mode 100644
index 0000000000..679f80f55c
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/mapper/SearchVisibleContactDtoMapper.java
@@ -0,0 +1,11 @@
+package org.entcore.communication.mapper;
+
+import io.vertx.core.json.JsonObject;
+import org.entcore.communication.dto.rest.SearchVisibleContactDTO;
+
+public class SearchVisibleContactDtoMapper {
+
+ public static SearchVisibleContactDTO map(JsonObject json) {
+ return new SearchVisibleContactDTO(json);
+ }
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/mapper/SearchVisibleDtoMapper.java b/communication/src/main/java/org/entcore/communication/mapper/SearchVisibleDtoMapper.java
new file mode 100644
index 0000000000..ebc243e7e5
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/mapper/SearchVisibleDtoMapper.java
@@ -0,0 +1,27 @@
+package org.entcore.communication.mapper;
+
+import io.vertx.core.json.JsonObject;
+import org.entcore.communication.dto.rest.GroupDTO;
+import org.entcore.communication.dto.rest.SearchVisibleResultDTO;
+import org.entcore.communication.dto.rest.UserDTO;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class SearchVisibleDtoMapper {
+
+ public static SearchVisibleResultDTO map(JsonObject json) {
+ List groups = json.getJsonArray("groups").stream()
+ .map(JsonObject.class::cast)
+ .map(GroupDtoMapper::map)
+ .collect(Collectors.toList());
+
+ List users = json.getJsonArray("users").stream()
+ .map(JsonObject.class::cast)
+ .map(UserDtoMapper::map)
+ .collect(Collectors.toList());
+
+ return new SearchVisibleResultDTO(groups, users);
+ }
+
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/mapper/UserDtoMapper.java b/communication/src/main/java/org/entcore/communication/mapper/UserDtoMapper.java
new file mode 100644
index 0000000000..7f4180ab66
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/mapper/UserDtoMapper.java
@@ -0,0 +1,55 @@
+package org.entcore.communication.mapper;
+
+import io.vertx.core.json.JsonObject;
+import org.entcore.communication.dto.rest.IdentifiableDTO;
+import org.entcore.communication.dto.rest.UserDTO;
+
+import java.util.stream.Collectors;
+
+public class UserDtoMapper {
+
+ public static UserDTO mapDiscoverVisible(JsonObject json) {
+ UserDTO dto = new UserDTO();
+ dto.setId(json.getString("id"))
+ .setName(json.getString("name"));
+ dto.setDisplayName(json.getString("displayName"))
+ .setGroupDisplayName(json.getString("groupDisplayName"))
+ .setProfile(json.getString("profile"))
+ .setHasCommunication(json.getBoolean("hasCommunication"));
+ if (json.getJsonArray("structures") != null) {
+ dto.setStructures(json.getJsonArray("structures").getList());
+ }
+ return dto;
+ }
+
+ public static UserDTO mapDiscoverVisibleGroupUser(JsonObject json) {
+ UserDTO dto = new UserDTO();
+ dto.setId(json.getString("id"));
+ dto.setDisplayName(json.getString("displayName"))
+ .setType(json.getString("type"))
+ .setLogin(json.getString("login"))
+ .setHasCommunication(json.getBoolean("hasCommunication"));
+ return dto;
+ }
+
+ public static UserDTO map(JsonObject user) {
+ UserDTO dto = new UserDTO();
+ dto.setId(user.getString("id"));
+ dto.setName(user.getString("name"));
+ dto.setDisplayName(user.getString("displayName"))
+ .setProfile(user.getString("profile"));
+ if (user.getJsonArray("subjects") != null) {
+ dto.setSubjects( user.getJsonArray("subjects").getList());
+ }
+ if(user.getJsonArray("positions") != null) {
+ dto.setPositions(user.getJsonArray("positions").stream()
+ .map(JsonObject.class::cast)
+ .map( o -> new IdentifiableDTO(o.getString("id"), o.getString("name")))
+ .collect(Collectors.toList()));
+ }
+ dto.setType(user.getString("type"))
+ .setLogin(user.getString("login"));
+ return dto;
+ }
+
+}
diff --git a/communication/src/main/java/org/entcore/communication/mapper/VerifyResultDtoMapper.java b/communication/src/main/java/org/entcore/communication/mapper/VerifyResultDtoMapper.java
new file mode 100644
index 0000000000..68284291f8
--- /dev/null
+++ b/communication/src/main/java/org/entcore/communication/mapper/VerifyResultDtoMapper.java
@@ -0,0 +1,11 @@
+package org.entcore.communication.mapper;
+
+import io.vertx.core.json.JsonObject;
+import org.entcore.communication.dto.rest.VerifyResultDTO;
+
+public class VerifyResultDtoMapper {
+
+ public static VerifyResultDTO map(JsonObject json) {
+ return new VerifyResultDTO(json);
+ }
+}
\ No newline at end of file
diff --git a/communication/src/main/java/org/entcore/communication/services/CommunicationService.java b/communication/src/main/java/org/entcore/communication/services/CommunicationService.java
index c2057889ca..2d0a0d9a93 100644
--- a/communication/src/main/java/org/entcore/communication/services/CommunicationService.java
+++ b/communication/src/main/java/org/entcore/communication/services/CommunicationService.java
@@ -31,6 +31,10 @@
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
+import org.entcore.communication.dto.rest.DiscoverModifyGroupUsersDTO;
+import org.entcore.communication.dto.rest.DiscoverVisibleGroupBodyDTO;
+import org.entcore.communication.dto.rest.DiscoverVisibleFilterDTO;
+import org.entcore.communication.dto.rest.SearchVisibleRequestDTO;
public interface CommunicationService {
String IMPOSSIBLE_TO_CHANGE_DIRECTION = "impossible to change direction";
@@ -98,41 +102,58 @@ static public Direction fromBitmask(int bitmask) {
void addLink(String startGroupId, String endGroupId,
Handler> handler);
+ Future addLink(String startGroupId, String endGroupId);
+
void removeLink(String startGroupId, String endGroupId,
Handler> handler);
void addLinkWithUsers(String groupId, Direction direction,
Handler> handler);
+ Future addLinkWithUsers(String groupId, Direction direction);
+
void addLinkWithUsers(Map params,
Handler> handler);
void removeLinkWithUsers(String groupId, Direction direction,
Handler> handler);
+ Future removeLinkWithUsers(String groupId, Direction direction);
+
void communiqueWith(String groupId,// VisibleType filter,
Handler> handler);
- void addLinkBetweenRelativeAndStudent(String groupId, Direction direction,
- Handler> handler);
+ Future communiqueWith(String groupId);
- void removeLinkBetweenRelativeAndStudent(String groupId, Direction direction,
- Handler> handler);
+ Future addLinkBetweenRelativeAndStudent(String groupId, Direction direction);
+
+ Future removeLinkBetweenRelativeAndStudent(String groupId, Direction direction);
void initDefaultRules(JsonArray structureIds, JsonObject defaultRules, final Integer transactionId,
final Boolean commit, final Handler> handler);
+ Future initDefaultRules(JsonArray structureIds, JsonObject defaultRules, Integer transactionId,
+ Boolean commit);
+
void initDefaultRules(JsonArray structureIds, JsonObject defaultRules,
Handler> handler);
+ Future initDefaultRules(JsonArray structureIds, JsonObject defaultRules);
+
void applyDefaultRules(JsonArray structureIds, final Integer transactionId, final Boolean commit,
Handler> handler);
+ Future applyDefaultRules(JsonArray structureIds, Integer transactionId, Boolean commit);
+
void applyDefaultRules(JsonArray structureIds, Handler> handler);
+ Future applyDefaultRules(JsonArray structureIds);
+
void applyRules(String groupId, Handler> responseHandler);
- void removeRules(String structureId, Handler> handler);
+ Future applyRules(String groupId);
+
+ Future removeRules(String structureId);
void visibleUsers(String userId, String structureId, JsonArray expectedTypes, boolean itSelf, boolean myGroup,
boolean profile, String preFilter, String customReturn, JsonObject additionalParams,
@@ -143,27 +164,42 @@ void visibleUsers(String userId, String structureId, JsonArray expectedTypes, bo
boolean reverseUnion,
Handler> handler);
+ Future visibleUsers(String userId, String structureId, JsonArray expectedTypes, boolean itSelf,
+ boolean myGroup, boolean profile, String preFilter, String customReturn, JsonObject additionalParams,
+ String userProfile, boolean reverseUnion);
+
+ Future visibleUsers(UserInfos userInfos, SearchVisibleRequestDTO searchVisibleDto);
+
void usersCanSeeMe(String userId, final Handler> handler);
+ Future usersCanSeeMe(String userId);
+
void visibleProfilsGroups(String userId, String customReturn, JsonObject additionnalParams, String preFilter,
Handler> handler);
+ Future visibleProfilsGroups(String userId, String customReturn, JsonObject additionnalParams,
+ String preFilter);
+
void visibleManualGroups(String userId, String customReturn, JsonObject additionnalParams,
Handler> handler);
- void getOutgoingRelations(String id, Handler> results);
+ Future visibleManualGroups(String userId, String customReturn, JsonObject additionnalParams);
+
+ Future visibleUsersForShare(String userId, String search, JsonArray userIds);
+
+ Future getOutgoingRelations(String id);
- void getIncomingRelations(String id, Handler> results);
+ Future getIncomingRelations(String id);
- void safelyRemoveLinkWithUsers(String groupId, Handler> handler);
+ Future safelyRemoveLinkWithUsers(String groupId);
void getDirections(String startGroupId, String endGroupId, Handler> handler);
- void addLinkCheckOnly(String startGroupId, String endGroupId, UserInfos userInfos, Handler> handler);
+ Future addLinkCheckOnly(String startGroupId, String endGroupId, UserInfos userInfos);
- void processChangeDirectionAfterAddingLink(String startGroupId, String endGroupId, Handler> handler);
+ Future processChangeDirectionAfterAddingLink(String startGroupId, String endGroupId);
- void removeRelations(String startGroupId, String endGroupId, Handler> handler);
+ Future removeRelations(String startGroupId, String endGroupId);
/**
* Indicates if a sender (user) can communicate to a receiver (user or group) on using
@@ -176,27 +212,27 @@ void visibleManualGroups(String userId, String customReturn, JsonObject addition
* @param recipientId id of the recipient
* @param handler final handler
*/
- void verify(String senderId, String recipientId, Handler> handler);
+ Future verify(String senderId, String recipientId);
- void getDiscoverVisibleUsers(String userId, JsonObject filter, final Handler> handler);
+ Future getDiscoverVisibleUsers(String userId, DiscoverVisibleFilterDTO filter);
- void getDiscoverVisibleStructures(final Handler> handler);
+ Future getDiscoverVisibleStructures();
- void discoverVisibleAddCommuteUsers(UserInfos user, String recipientId, HttpServerRequest request, Handler> handler);
+ Future discoverVisibleAddCommuteUsers(UserInfos user, String recipientId, HttpServerRequest request);
- void discoverVisibleRemoveCommuteUsers(String senderId, String recipientId, Handler> handler);
+ Future discoverVisibleRemoveCommuteUsers(String senderId, String recipientId);
- void discoverVisibleGetGroups(String userId, Handler> handler);
+ Future discoverVisibleGetGroups(String userId);
- void discoverVisibleGetUsersInGroup(String userId, String groupId, Handler> handler);
+ Future discoverVisibleGetUsersInGroup(String userId, String groupId);
- void createDiscoverVisibleGroup(String userId, JsonObject body, Handler> handler);
+ Future createDiscoverVisibleGroup(String userId, DiscoverVisibleGroupBodyDTO body);
- void updateDiscoverVisibleGroup(String userId, String groupId, JsonObject body, Handler> handler);
+ Future updateDiscoverVisibleGroup(String userId, String groupId, DiscoverVisibleGroupBodyDTO body);
- void addDiscoverVisibleGroupUsers(UserInfos user, String groupId, JsonObject body, HttpServerRequest request, Handler> handler);
+ Future addDiscoverVisibleGroupUsers(UserInfos user, String groupId, DiscoverModifyGroupUsersDTO body, HttpServerRequest request);
- void getDiscoverVisibleAcceptedProfile(UserInfos user, Handler> handler);
+ Future getDiscoverVisibleAcceptedProfile(UserInfos user);
/**
diff --git a/communication/src/main/java/org/entcore/communication/services/impl/DefaultCommunicationService.java b/communication/src/main/java/org/entcore/communication/services/impl/DefaultCommunicationService.java
index 59072e61f0..f702dc3112 100644
--- a/communication/src/main/java/org/entcore/communication/services/impl/DefaultCommunicationService.java
+++ b/communication/src/main/java/org/entcore/communication/services/impl/DefaultCommunicationService.java
@@ -30,6 +30,7 @@
import io.vertx.core.impl.logging.LoggerFactory;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
+import org.apache.commons.collections4.CollectionUtils;
import org.entcore.common.conversation.LegacySearchVisibleRequest;
import org.entcore.common.neo4j.Neo4j;
import org.entcore.common.neo4j.StatementsBuilder;
@@ -39,12 +40,18 @@
import org.entcore.common.user.UserUtils;
import org.entcore.common.utils.StringUtils;
import org.entcore.common.validation.StringValidation;
+import org.entcore.communication.dto.rest.DiscoverVisibleGroupBodyDTO;
+import org.entcore.communication.dto.rest.DiscoverModifyGroupUsersDTO;
+import org.entcore.communication.dto.rest.DiscoverVisibleFilterDTO;
+import org.entcore.communication.dto.rest.SearchVisibleRequestDTO;
import org.entcore.communication.services.CommunicationService;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
+import static fr.wseduc.webutils.Utils.eitherToFuture;
+import static fr.wseduc.webutils.Utils.isNotEmpty;
import static io.vertx.core.json.JsonObject.mapFrom;
import static org.entcore.common.neo4j.Neo4jResult.*;
import static org.entcore.common.share.ShareService.EXPECTED_IDS_USERS_GROUPS;
@@ -378,8 +385,21 @@ public void communiqueWith(String groupId, Handler> h
}
@Override
- public void addLinkBetweenRelativeAndStudent(String groupId, Direction direction,
- Handler> handler) {
+ public Future communiqueWith(String groupId) {
+ Promise promise = Promise.promise();
+ communiqueWith(groupId, r -> {
+ if (r.isLeft()) {
+ promise.fail(r.left().getValue());
+ } else {
+ promise.complete(r.right().getValue());
+ }
+ });
+ return promise.future();
+ }
+
+ @Override
+ public Future addLinkBetweenRelativeAndStudent(String groupId, Direction direction) {
+ Promise promise = Promise.promise();
String createRelationship;
switch (direction) {
case INCOMING:
@@ -397,12 +417,19 @@ public void addLinkBetweenRelativeAndStudent(String groupId, Direction direction
"CREATE UNIQUE " + createRelationship +
"RETURN COUNT(*) as number ";
JsonObject params = new JsonObject().put("groupId", groupId).put("direction", direction.name());
- neo4j.execute(query, params, validUniqueResultHandler(handler));
+ neo4j.execute(query, params, validUniqueResultHandler( res -> {
+ if(res.isLeft()) {
+ promise.fail(res.left().getValue());
+ return;
+ }
+ promise.complete(res.right().getValue().getInteger("number"));
+ }));
+ return promise.future();
}
@Override
- public void removeLinkBetweenRelativeAndStudent(String groupId, Direction direction,
- Handler> handler) {
+ public Future removeLinkBetweenRelativeAndStudent(String groupId, Direction direction) {
+ Promise promise = Promise.promise();
String relationship;
String set;
switch (direction) {
@@ -429,7 +456,14 @@ public void removeLinkBetweenRelativeAndStudent(String groupId, Direction direct
"DELETE r " +
"RETURN COUNT(*) as number ";
JsonObject params = new JsonObject().put("groupId", groupId);
- neo4j.execute(query, params, validUniqueResultHandler(handler));
+ neo4j.execute(query, params, validUniqueResultHandler(res -> {
+ if(res.isLeft()) {
+ promise.fail(res.left().getValue());
+ return;
+ }
+ promise.complete(res.right().getValue().getInteger("number"));
+ }));
+ return promise.future();
}
private List getStatementsForDefaultRules(JsonArray structureIds, JsonObject defaultRules) {
@@ -504,6 +538,47 @@ public void initDefaultRules(JsonArray structureIds, JsonObject defaultRules,
initDefaultRules(structureIds, defaultRules, null, true, handler);
}
+ @Override
+ public Future initDefaultRules(JsonArray structureIds, JsonObject defaultRules) {
+ return eitherToFuture(h -> initDefaultRules(structureIds, defaultRules, h));
+ }
+
+ @Override
+ public Future initDefaultRules(JsonArray structureIds, JsonObject defaultRules,
+ Integer transactionId, Boolean commit) {
+ return eitherToFuture(h -> initDefaultRules(structureIds, defaultRules, transactionId, commit, h));
+ }
+
+ @Override
+ public Future applyDefaultRules(JsonArray structureIds) {
+ return eitherToFuture(h -> applyDefaultRules(structureIds, h));
+ }
+
+ @Override
+ public Future applyDefaultRules(JsonArray structureIds, Integer transactionId, Boolean commit) {
+ return eitherToFuture(h -> applyDefaultRules(structureIds, transactionId, commit, h));
+ }
+
+ @Override
+ public Future applyRules(String groupId) {
+ return eitherToFuture(h -> applyRules(groupId, h));
+ }
+
+ @Override
+ public Future addLink(String startGroupId, String endGroupId) {
+ return eitherToFuture(h -> addLink(startGroupId, endGroupId, h));
+ }
+
+ @Override
+ public Future addLinkWithUsers(String groupId, Direction direction) {
+ return eitherToFuture(h -> addLinkWithUsers(groupId, direction, h));
+ }
+
+ @Override
+ public Future removeLinkWithUsers(String groupId, Direction direction) {
+ return eitherToFuture(h -> removeLinkWithUsers(groupId, direction, h));
+ }
+
private void getStatementsForDefaultRules(JsonArray structureIds, String attr, JsonObject defaultRules,
final StatementsBuilder existingGroups, final StatementsBuilder newGroups) {
final String[] a = attr.split("\\-");
@@ -721,7 +796,7 @@ public void applyRules(String groupId, Handler> handl
}
@Override
- public void removeRules(String structureId, Handler> handler) {
+ public Future removeRules(String structureId) {
String query;
JsonObject params = new JsonObject();
if (structureId != null && !structureId.trim().isEmpty()) {
@@ -736,7 +811,7 @@ public void removeRules(String structureId, Handler>
"OPTIONAL MATCH ()-[r1:COMMUNIQUE_DIRECT]->() " +
"DELETE r, r1 ";
}
- neo4j.execute(query, params, validEmptyHandler(handler));
+ return eitherToFuture(h -> neo4j.execute(query, params, validEmptyHandler(h)));
}
@Override
@@ -864,6 +939,105 @@ public void visibleUsers(
neo4j.execute(q, params, validResultHandler(handler));
}
+ @Override
+ public Future visibleUsers(UserInfos userInfos, SearchVisibleRequestDTO searchVisibleDto) {
+ Promise promise = Promise.promise();
+ String preFilter = "";
+ String match = "";
+ String where = "";
+ String nbUsers = "";
+ String groupTypes = "";
+ JsonObject params = new JsonObject();
+ JsonArray expectedTypes = null;
+
+ boolean matchAdded = false;
+
+ if (!CollectionUtils.isEmpty(searchVisibleDto.getStructures())) {
+
+ match = "MATCH ";
+ where = " WHERE ";
+ match += "(visibles)-[:IN*0..1]->()-[:DEPENDS*1..2]->(n) ";
+ where += "n.id IN {nIds} ";
+
+ params.put("nIds", new JsonArray(searchVisibleDto.getStructures()));
+ matchAdded = true;
+ } else if (!CollectionUtils.isEmpty(searchVisibleDto.getClasses())) {
+
+ match = "MATCH ";
+ where = " WHERE ";
+ match += "(visibles)-[:IN*0..1]->()-[:DEPENDS*1..2]->(n) ";
+ where += "n.id IN {nIds} ";
+
+ params.put("nIds", new JsonArray(searchVisibleDto.getClasses()));
+ matchAdded = true;
+ }
+ if (!CollectionUtils.isEmpty(searchVisibleDto.getProfiles())) {
+ params.put("filters", new JsonArray(searchVisibleDto.getProfiles()));
+ if (!matchAdded) {
+ match = "MATCH (visibles)-[:IN*0..1]->(g)";
+ where = " WHERE g.filter IN {filters} ";
+ } else {
+ match += " MATCH (visibles)-[:IN*0..1]->(g)";
+ where += " AND g.filter IN {filters} ";
+ }
+ matchAdded = true;
+ }
+ if (!CollectionUtils.isEmpty(searchVisibleDto.getFunctions())) {
+ params.put("filters2", new JsonArray(searchVisibleDto.getFunctions()));
+ if (!matchAdded) {
+ match = "MATCH (visibles)-[:IN*0..1]->(g2)";
+ where = " WHERE g2.filter IN {filters2} ";
+ } else {
+ match += " MATCH (visibles)-[:IN*0..1]->(g2)";
+ where += " AND g2.filter IN {filters2} ";
+ }
+ matchAdded = true;
+ }
+ if (!CollectionUtils.isEmpty(searchVisibleDto.getPositions())) {
+ params.put("positionIds", new JsonArray(searchVisibleDto.getPositions()));
+ if (!matchAdded) {
+ where = " WHERE ";
+ } else {
+ where += " AND ";
+ }
+ where += " ANY(id IN positionIds WHERE id IN {positionIds}) ";
+ matchAdded = true;
+ }
+ if (isNotEmpty(searchVisibleDto.getSearch())) {
+ preFilter = "AND m.displayNameSearchField CONTAINS {search} ";
+
+ String sanitizedSearch = StringValidation.sanitize(searchVisibleDto.getSearch());
+ params.put("search", sanitizedSearch);
+ }
+ if (CollectionUtils.isEmpty(searchVisibleDto.getTypes())) {
+ if (CommunicationService.EXPECTED_TYPES.containsAll(searchVisibleDto.getTypes())) {
+ expectedTypes = new JsonArray(searchVisibleDto.getTypes());
+ }
+ }
+ if (Boolean.TRUE.equals(searchVisibleDto.getNbUsersInGroups())) {
+ nbUsers = ", visibles.nbUsers as nbUsers";
+ }
+ if (Boolean.TRUE.equals(searchVisibleDto.getGroupType())) {
+ groupTypes = ", labels(visibles) as groupType, visibles.filter as groupProfile";
+ }
+
+ final String customReturn = match + where +
+ "RETURN DISTINCT visibles.id as id, visibles.name as name, " +
+ "positionNames, positionIds, " +
+ "visibles.displayName as displayName, visibles.groupDisplayName as groupDisplayName, " +
+ "HEAD(visibles.profiles) as profile, subjects" + nbUsers + groupTypes;
+
+ visibleUsers(userInfos.getUserId(), null, expectedTypes, searchVisibleDto.isItSelf(), searchVisibleDto.isMyGroup(), searchVisibleDto.isProfile(),
+ preFilter, customReturn, params, userInfos.getType(), false, visibles -> {
+ if (visibles.isRight()) {
+ promise.complete(visibles.right().getValue());
+ } else {
+ promise.fail(visibles.left().getValue());
+ }
+ });
+ return promise.future();
+ }
+
@Override
public void usersCanSeeMe(String userId, Handler> handler) {
@@ -935,6 +1109,35 @@ public void visibleManualGroups(String userId, String customReturn, JsonObject a
neo4j.execute(query, params, validResultHandler(handler));
}
+ @Override
+ public Future visibleUsers(String userId, String structureId, JsonArray expectedTypes, boolean itSelf,
+ boolean myGroup, boolean profile, String preFilter, String customReturn, JsonObject additionalParams,
+ String userProfile, boolean reverseUnion) {
+ return eitherToFuture(h -> visibleUsers(userId, structureId, expectedTypes, itSelf, myGroup, profile,
+ preFilter, customReturn, additionalParams, userProfile, reverseUnion, h));
+ }
+
+ @Override
+ public Future usersCanSeeMe(String userId) {
+ return eitherToFuture(h -> usersCanSeeMe(userId, h));
+ }
+
+ @Override
+ public Future visibleProfilsGroups(String userId, String customReturn, JsonObject additionnalParams,
+ String preFilter) {
+ return eitherToFuture(h -> visibleProfilsGroups(userId, customReturn, additionnalParams, preFilter, h));
+ }
+
+ @Override
+ public Future visibleManualGroups(String userId, String customReturn, JsonObject additionnalParams) {
+ return eitherToFuture(h -> visibleManualGroups(userId, customReturn, additionnalParams, h));
+ }
+
+ @Override
+ public Future visibleUsersForShare(String userId, String search, JsonArray userIds) {
+ return eitherToFuture(h -> visibleUsersForShare(userId, search, userIds, h));
+ }
+
private static String relationQuery = "OPTIONAL MATCH (sg:Structure)<-[:DEPENDS]-(g) "
+ "OPTIONAL MATCH (sc:Structure)<-[:BELONGS]-(c:Class)<-[:DEPENDS]-(g) "
+ "WITH COALESCE(sg, sc) as s, c, g "
@@ -956,26 +1159,44 @@ public void visibleManualGroups(String userId, String customReturn, JsonObject a
+ " WHEN HAS(g.subType) THEN g.subType END as subType";
@Override
- public void getOutgoingRelations(String id, Handler> results) {
+ public Future getOutgoingRelations(String id) {
+ Promise promise = Promise.promise();
String query = "MATCH (g:Group)<-[:COMMUNIQUE]-(ug: Group { id: {id} }) WHERE exists(g.id) "
+ relationQuery;
JsonObject params = new JsonObject().put("id", id);
- neo4j.execute(query, params, validResultHandler(results));
+ neo4j.execute(query, params, validResultHandler( v -> {
+ if(v.isLeft()) {
+ promise.fail(v.left().getValue());
+ return;
+ }
+ promise.complete(v.right().getValue());
+ }));
+
+ return promise.future();
}
@Override
- public void getIncomingRelations(String id, Handler> results) {
+ public Future getIncomingRelations(String id) {
+ Promise promise = Promise.promise();
String query = "MATCH (g:Group)-[:COMMUNIQUE]->(ug: Group { id: {id} }) WHERE exists(g.id) "
+ relationQuery;
JsonObject params = new JsonObject().put("id", id);
- neo4j.execute(query, params, validResultHandler(results));
+ neo4j.execute(query, params, validResultHandler(v -> {
+ if(v.isLeft()) {
+ promise.fail(v.left().getValue());
+ return;
+ }
+ promise.complete(v.right().getValue());
+ }));
+ return promise.future();
}
@Override
- public void safelyRemoveLinkWithUsers(String groupId, Handler> handler) {
+ public Future safelyRemoveLinkWithUsers(String groupId) {
+ Promise promise = Promise.promise();
getRelationsOfGroup(groupId).whenComplete((result, err) -> {
if (err != null) {
- handler.handle(new Either.Left<>(err.getMessage()));
+ promise.fail(err.getMessage());
} else {
int numberOfSendingGroups = result.getInteger("numberOfSendingGroups");
int numberOfReceivingGroups = result.getInteger("numberOfReceivingGroups");
@@ -983,14 +1204,15 @@ public void safelyRemoveLinkWithUsers(String groupId, Handler 0, numberOfReceivingGroups > 0);
if (directionToRemove == null) {
- handler.handle(new Either.Left<>(CommunicationService.IMPOSSIBLE_TO_CHANGE_DIRECTION));
+ promise.fail(CommunicationService.IMPOSSIBLE_TO_CHANGE_DIRECTION);
} else {
Direction nextDirection = computeNextDirection(directionToRemove);
removeLinkWithUsers(groupId, directionToRemove,
- t -> handler.handle(new Either.Right<>(new JsonObject().put("users", nextDirection))));
+ t -> promise.complete(new JsonObject().put("users", nextDirection)));
}
}
});
+ return promise.future();
}
@Override
@@ -1115,11 +1337,8 @@ protected Future computeWarningMessageForAddLinkCheck(
}
@Override
- public void addLinkCheckOnly(String startGroupId, String endGroupId, UserInfos userInfos, Handler> handler) {
- // 1. Get group info (Direction, Type and Subtype)
- getGroupInfos(startGroupId, endGroupId)
- // 2. Compute next directions
- // 3. Check for impossible direction changes
+ public Future addLinkCheckOnly(String startGroupId, String endGroupId, UserInfos userInfos) {
+ return getGroupInfos(startGroupId, endGroupId)
.compose(groupsInfos -> computeWarningMessageForAddLinkCheck(
userInfos,
groupsInfos[0],
@@ -1127,9 +1346,7 @@ public void addLinkCheckOnly(String startGroupId, String endGroupId, UserInfos u
groupsInfos[1],
computeDirectionForAddLinkCheck(groupsInfos[1], false)
))
- // 4. Compute warning message
- .onSuccess(msg -> handler.handle(new Either.Right<>(new JsonObject().put("warning", msg))))
- .onFailure(err -> handler.handle(new Either.Left(err.getMessage())));
+ .map(msg -> new JsonObject().put("warning", msg));
}
private CompletableFuture getRelationsOfGroup(String groupId) {
@@ -1151,10 +1368,11 @@ private CompletableFuture getRelationsOfGroup(String groupId) {
}
@Override
- public void removeRelations(String sendingGroupId, String receivingGroupId, Handler> handler) {
+ public Future removeRelations(String sendingGroupId, String receivingGroupId) {
+ Promise promise = Promise.promise();
this.removeLink(sendingGroupId, receivingGroupId, r -> {
if (r.isLeft()) {
- handler.handle(r);
+ promise.fail(r.left().getValue());
} else {
List> futures = new ArrayList<>();
futures.add(getRelationsOfGroup(sendingGroupId)
@@ -1200,36 +1418,30 @@ public void removeRelations(String sendingGroupId, String receivingGroupId, Hand
.thenApply(a -> futures.stream().map(CompletableFuture::join).collect(Collectors.toList()))
.whenComplete((re, err) -> {
if (err != null) {
- handler.handle(new Either.Left<>(err.getMessage()));
+ promise.fail(err.getMessage());
} else {
Direction senderDirection = re.get(0);
Direction receiverDirection = re.get(1);
-
- handler.handle(new Either.Right<>(new JsonObject()
+ promise.complete(new JsonObject()
.put("sender", senderDirection != null ? senderDirection.toString() : null)
- .put("receiver", receiverDirection != null ? receiverDirection.toString() : null)
- ));
+ .put("receiver", receiverDirection != null ? receiverDirection.toString() : null));
}
});
}
});
+ return promise.future();
}
@Override
- public void processChangeDirectionAfterAddingLink(String startGroupId, String endGroupId, Handler> handler) {
- // 1. Get group info (Direction, Type and Subtype)
- getGroupInfos(startGroupId, endGroupId)
- // 2. Compute next directions
- // 3. Apply direction changes
+ public Future processChangeDirectionAfterAddingLink(String startGroupId, String endGroupId) {
+ return getGroupInfos(startGroupId, endGroupId)
.compose(groupsInfos -> {
final Direction fromStartDirection = Direction.fromString(groupsInfos[0].getString("internalCommunicationRule"));
final Direction fromEndDirection = Direction.fromString(groupsInfos[1].getString("internalCommunicationRule"));
final Direction toStartDirection = computeDirectionForAddLinkCheck(groupsInfos[0], true);
final Direction toEndDirection = computeDirectionForAddLinkCheck(groupsInfos[1], false);
- // Check if any rule was added to the start group
Direction addedStartRule = Direction.fromBitmask(toStartDirection.bitmask & ~fromStartDirection.bitmask);
- // Check if any rule was added to the end group
Direction addedEndRule = Direction.fromBitmask(toEndDirection.bitmask & ~fromEndDirection.bitmask);
if (addedStartRule.equals(Direction.NONE) && addedEndRule.equals(Direction.NONE)) {
@@ -1255,10 +1467,7 @@ public void processChangeDirectionAfterAddingLink(String startGroupId, String en
});
return promise.future();
}
- })
- // 4. Compute warning message
- .onSuccess(result -> handler.handle(new Either.Right(result)))
- .onFailure(err -> handler.handle(new Either.Left(err.getMessage())));
+ });
}
public Direction computeDirectionToRemove(boolean hasIncomingRelationship, boolean hasOutgoingRelationship) {
@@ -1309,7 +1518,11 @@ public boolean isImpossibleToChangeDirectionGroupForAddLink(String filter, Strin
}
@Override
- public void verify(String senderId, String recipientId, Handler> handler) {
+ public Future verify(String senderId, String recipientId) {
+ return eitherToFuture(h -> verifyCallback(senderId, recipientId, h));
+ }
+
+ private void verifyCallback(String senderId, String recipientId, Handler> handler) {
String query = "MATCH (s:User), (r:User) "
+ "where s.id = {senderId} and r.id = {recipientId} "
@@ -1357,60 +1570,35 @@ public void verify(String senderId, String recipientId, Handler> handler) {
-
+ public Future getDiscoverVisibleUsers(String userId, DiscoverVisibleFilterDTO filter) {
JsonObject params = new JsonObject().put("userId", userId);
StringBuilder query = new StringBuilder("MATCH (m:Visible) "
+ "WHERE (NOT(HAS(m.blocked)) OR m.blocked = false) AND m.id <> {userId} ");
- if (filters != null && !filters.isEmpty()) {
- for (String key : filters.fieldNames()) {
- switch (key) {
- case "structures":
- JsonArray structures = filters.getJsonArray(key);
- if (structures != null && !structures.isEmpty()) {
- query.append("AND ANY(x IN m.structures WHERE x IN {structures}) ");
- params.put("structures", structures);
- }
- break;
- case "profiles":
- JsonArray profile = filters.getJsonArray(key);
- boolean allowProfileFilter = true;
-
- if(profile != null && !profile.isEmpty()) {
- for (Object p : profile) {
- if (!discoverVisibleExpectedProfile.contains(p)) {
- allowProfileFilter = false;
- break;
- }
- }
- } else {
- allowProfileFilter = false;
- }
+ if (filter != null && filter.getStructures() != null && !filter.getStructures().isEmpty()) {
+ JsonArray structures = new JsonArray(filter.getStructures());
+ query.append("AND ANY(x IN m.structures WHERE x IN {structures}) ");
+ params.put("structures", structures);
+ }
- if (allowProfileFilter) {
- query.append("AND HEAD(m.profiles) IN {profiles} ");
- params.put("profiles", profile);
- } else {
- query.append("AND HEAD(m.profiles) IN {discoverVisibleExpectedProfile} ");
- params.put("discoverVisibleExpectedProfile", discoverVisibleExpectedProfile);
- }
- break;
- case "search":
- final String search = filters.getString(key);
- if (search != null && !search.trim().isEmpty()) {
- query.append("AND m.displayNameSearchField CONTAINS {search} ");
- String sanitizedSearch = StringValidation.sanitize(search);
- params.put("search", sanitizedSearch);
- }
- break;
- }
+ if (filter != null && filter.getProfiles() != null && !filter.getProfiles().isEmpty()) {
+ JsonArray profiles = new JsonArray(filter.getProfiles());
+ boolean allowProfileFilter = profiles.stream().allMatch(discoverVisibleExpectedProfile::contains);
+ if (allowProfileFilter) {
+ query.append("AND HEAD(m.profiles) IN {profiles} ");
+ params.put("profiles", profiles);
+ } else {
+ query.append("AND HEAD(m.profiles) IN {discoverVisibleExpectedProfile} ");
+ params.put("discoverVisibleExpectedProfile", discoverVisibleExpectedProfile);
}
+ } else {
+ query.append("AND HEAD(m.profiles) IN {discoverVisibleExpectedProfile} ");
+ params.put("discoverVisibleExpectedProfile", discoverVisibleExpectedProfile);
}
- if(filters == null || filters.isEmpty() || !filters.containsKey("profiles")) {
- query.append("AND HEAD(m.profiles) in {discoverVisibleExpectedProfile} ");
- params.put("discoverVisibleExpectedProfile", discoverVisibleExpectedProfile);
+ if (filter != null && filter.getSearch() != null && !filter.getSearch().trim().isEmpty()) {
+ query.append("AND m.displayNameSearchField CONTAINS {search} ");
+ params.put("search", StringValidation.sanitize(filter.getSearch()));
}
query.append("WITH DISTINCT m as visibles "
@@ -1418,18 +1606,18 @@ public void getDiscoverVisibleUsers(String userId, JsonObject filters, final Han
+ "return DISTINCT visibles.id as id, visibles.name as name, visibles.displayName as displayName, "
+ "visibles.groupDisplayName as groupDisplayName, HEAD(visibles.profiles) as profile, visibles.structures as structures, u IS NOT NULL as hasCommunication");
- neo4j.execute(query.toString(), params, validResultHandler(handler));
+ return eitherToFuture(h -> neo4j.execute(query.toString(), params, validResultHandler(h)));
}
/**
* Return the list of structures
* */
@Override
- public void getDiscoverVisibleStructures(final Handler> handler) {
+ public Future getDiscoverVisibleStructures() {
String query = "MATCH (s:Structure) "
+ "RETURN s.id as id, s.externalId as type, s.name as label, 'false' as checked";
- neo4j.execute(query, new JsonObject(), validResultHandler(handler));
+ return eitherToFuture(h -> neo4j.execute(query, new JsonObject(), validResultHandler(h)));
}
@@ -1437,77 +1625,70 @@ public void getDiscoverVisibleStructures(final Handler
* Add communication between two users, using the COMMUNIQUE_DIRECT relation and source 'MANUAL'
* */
@Override
- public void discoverVisibleAddCommuteUsers(UserInfos user, String recipientId, HttpServerRequest request, Handler> handler){
-
+ public Future discoverVisibleAddCommuteUsers(UserInfos user, String recipientId, HttpServerRequest request) {
String query = "MATCH (s:User {id : {senderId}}), (r:User {id : {recipientId}}) WHERE HEAD(s.profiles) IN {discoverVisibleExpectedProfile} AND HEAD(r.profiles) IN {discoverVisibleExpectedProfile} "
+ "OPTIONAL MATCH (s)-[rel:COMMUNIQUE_DIRECT]->(r) WITH s, r, COUNT(rel) AS relCount "
+ "WHERE relCount = 0 CREATE (s)-[:COMMUNIQUE_DIRECT {source: 'MANUAL'}]->(r) RETURN COUNT(*) AS number ";
JsonObject params = new JsonObject().put("senderId", user.getUserId()).put("recipientId", recipientId).put("discoverVisibleExpectedProfile", discoverVisibleExpectedProfile);
- neo4j.execute(query, params, validUniqueResultHandler(result -> {
+ return eitherToFuture(h -> neo4j.execute(query, params, validUniqueResultHandler(result -> {
if (result.isRight() && !result.right().getValue().isEmpty()) {
- if(result.right().getValue().getInteger("number") > 0) {
+ if (result.right().getValue().getInteger("number") > 0) {
sendNotificationTimeline(request, user, new JsonArray().add(recipientId), "");
}
- handler.handle(new Either.Right<>(result.right().getValue()));
- } else {
- if (result.isLeft()) {
- handler.handle(new Either.Left<>(result.left().getValue()));
-
- }
+ h.handle(new Either.Right<>(result.right().getValue()));
+ } else if (result.isLeft()) {
+ h.handle(new Either.Left<>(result.left().getValue()));
}
- }));
+ })));
}
/**
* Remove communication between two users, using the COMMUNIQUE_DIRECT relation and source 'MANUAL'
* */
@Override
- public void discoverVisibleRemoveCommuteUsers(String senderId, String recipientId, Handler> handler){
-
+ public Future discoverVisibleRemoveCommuteUsers(String senderId, String recipientId) {
String query = "MATCH (s:User {id : {senderId}})-[r:COMMUNIQUE_DIRECT { source: 'MANUAL'}]-(u:User {id : {recipientId}}) " +
"DELETE r " +
"RETURN COUNT(*) as number";
JsonObject params = new JsonObject().put("senderId", senderId).put("recipientId", recipientId);
- neo4j.execute(query, params, validUniqueResultHandler(handler));
+ return eitherToFuture(h -> neo4j.execute(query, params, validUniqueResultHandler(h)));
}
/**
* Return the list of groups, that the user is IN or has COMMUNIQUE rights, with group type 'manager'
* */
@Override
- public void discoverVisibleGetGroups(String userId, Handler> handler) {
+ public Future discoverVisibleGetGroups(String userId) {
String query = "MATCH (g:CommunityGroup:Group:Visible {type: 'manager'})<-[r:IN|COMMUNIQUE]-(u:User {id: {userId}}) " +
"RETURN DISTINCT g.id as id, g.name as name, g.displayNameSearchField as displayName, g.nbUsers as nbUsers ORDER BY name";
JsonObject params = new JsonObject().put("userId", userId);
- neo4j.execute(query, params, validResultHandler(handler));
+ return eitherToFuture(h -> neo4j.execute(query, params, validResultHandler(h)));
}
/**
* Return the list of users, that are IN the group, with group type 'manager'
* */
@Override
- public void discoverVisibleGetUsersInGroup(String userId, String groupId, Handler> handler) {
+ public Future