TeamManager.java
- /*
- * The coLAB project
- * Copyright (C) 2021-2024 AlbaSim, MEI, HEIG-VD, HES-SO
- *
- * Licensed under the MIT License
- */
- package ch.colabproject.colab.api.controller.team;
- import ch.colabproject.colab.api.controller.card.CardManager;
- import ch.colabproject.colab.api.controller.project.ProjectManager;
- import ch.colabproject.colab.api.controller.security.SecurityManager;
- import ch.colabproject.colab.api.controller.token.TokenManager;
- import ch.colabproject.colab.api.model.card.Card;
- import ch.colabproject.colab.api.model.project.Project;
- import ch.colabproject.colab.api.model.team.TeamMember;
- import ch.colabproject.colab.api.model.team.TeamRole;
- import ch.colabproject.colab.api.model.team.acl.HierarchicalPosition;
- import ch.colabproject.colab.api.model.user.User;
- import ch.colabproject.colab.api.persistence.jpa.team.TeamMemberDao;
- import ch.colabproject.colab.api.persistence.jpa.team.TeamRoleDao;
- import ch.colabproject.colab.generator.model.exceptions.HttpErrorMessage;
- import ch.colabproject.colab.generator.model.exceptions.MessageI18nKey;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import javax.ejb.LocalBean;
- import javax.ejb.Stateless;
- import javax.inject.Inject;
- import java.util.List;
- import java.util.Objects;
- import java.util.stream.Collectors;
- /**
- * Some logic to manage project teams
- *
- * @author maxence
- */
- @Stateless
- @LocalBean
- public class TeamManager {
- /** logger */
- private static final Logger logger = LoggerFactory.getLogger(TeamManager.class);
- /** Team members persistence */
- @Inject
- private TeamMemberDao teamMemberDao;
- /** Team roles persistence */
- @Inject
- private TeamRoleDao teamRoleDao;
- /** Project specific logic handling */
- @Inject
- private ProjectManager projectManager;
- /** Card specific logic handling */
- @Inject
- private CardManager cardManager;
- /** Token Facade */
- @Inject
- private TokenManager tokenManager;
- /** Access control manager */
- @Inject
- private SecurityManager securityManager;
- // *********************************************************************************************
- // find team member
- // *********************************************************************************************
- /**
- * Retrieve the team member. If not found, throw a {@link HttpErrorMessage}.
- *
- * @param memberId the id of the team member
- *
- * @return the team member if found
- *
- * @throws HttpErrorMessage if the team member was not found
- */
- public TeamMember assertAndGetMember(Long memberId) {
- TeamMember member = teamMemberDao.findTeamMember(memberId);
- if (member == null) {
- logger.error("team member #{} not found", memberId);
- throw HttpErrorMessage.dataError(MessageI18nKey.DATA_NOT_FOUND);
- }
- return member;
- }
- // *********************************************************************************************
- // team members
- // *********************************************************************************************
- /**
- * Add given user to the project teams
- *
- * @param project the project
- * @param user the user
- * @param position hierarchical position of the user
- *
- * @return the brand-new member
- */
- public TeamMember addMember(Project project, User user, HierarchicalPosition position) {
- logger.debug("Add member {} in {}", user, project);
- if (project == null) {
- throw HttpErrorMessage.dataError(MessageI18nKey.DATA_INTEGRITY_FAILURE);
- }
- if (user != null && findMemberByProjectAndUser(project, user) != null) {
- throw HttpErrorMessage.dataError(MessageI18nKey.DATA_INTEGRITY_FAILURE);
- }
- TeamMember teamMember = new TeamMember();
- teamMember.setUser(user);
- teamMember.setProject(project);
- teamMember.setPosition(position);
- teamMemberDao.persistTeamMember(teamMember);
- project.getTeamMembers().add(teamMember);
- return teamMember;
- }
- /**
- * Get the members of the given project
- *
- * @param projectId the id of the project
- *
- * @return list of team members
- */
- public List<TeamMember> getTeamMembersForProject(Long projectId) {
- logger.debug("Get team members of project #{}", projectId);
- Project project = projectManager.assertAndGetProject(projectId);
- return project.getTeamMembers();
- }
- /**
- * Get all members of the given project
- *
- * @param id id of the project
- *
- * @return all members of the project team
- */
- public List<TeamMember> getTeamMembers(Long id) {
- Project project = projectManager.assertAndGetProject(id);
- logger.debug("Get team members: {}", project);
- return project.getTeamMembers();
- }
- /**
- * Find the teamMember who match the given project and the given user.
- *
- * @param project the project
- * @param user the user
- *
- * @return the teamMember or null
- */
- public TeamMember findMemberByProjectAndUser(Project project, User user) {
- return teamMemberDao.findMemberByProjectAndUser(project, user);
- }
- /**
- * Retrieve all users of the team members
- *
- * @param projectId the id of the project
- *
- * @return list of users
- */
- public List<User> getUsersForProject(Long projectId) {
- return getTeamMembersForProject(projectId).stream()
- .filter(m -> {
- return m.getUser() != null;
- })
- .map(m -> {
- return m.getUser();
- })
- .collect(Collectors.toList());
- }
- /**
- * Are two user teammate?
- *
- * @param a a user
- * @param b another user
- *
- * @return true if both user are both member of the same team
- */
- public boolean areUserTeammate(User a, User b) {
- return teamMemberDao.findIfUserAreTeammate(a, b);
- }
- /**
- * Update hierarchical position of a member
- *
- * @param memberId id of the member
- * @param position new hierarchical position
- */
- public void updatePosition(Long memberId, HierarchicalPosition position) {
- TeamMember member = teamMemberDao.findTeamMember(memberId);
- if (member != null && position != null) {
- if (position == HierarchicalPosition.OWNER
- || member.getPosition() == HierarchicalPosition.OWNER) {
- assertCurrentUserIsOwnerOfTheProject(member.getProject());
- }
- member.setPosition(position);
- assertTeamIntegrity(member.getProject());
- } else {
- throw HttpErrorMessage.dataError(MessageI18nKey.DATA_INTEGRITY_FAILURE);
- }
- }
- /**
- * Make sure the team of a project make sense. It means:
- * <ul>
- * <li>at least one "owner"
- * </ul>
- *
- * @param project the project to check
- *
- * @throws HttpErrorMessage if team is broken
- */
- public void assertTeamIntegrity(Project project) {
- if (project.getTeamMembersByPosition(HierarchicalPosition.OWNER).isEmpty()) {
- throw HttpErrorMessage.dataError(MessageI18nKey.DATA_INTEGRITY_FAILURE);
- }
- }
- /**
- * Make sure the current user is an owner of the given project
- *
- * @param project the project
- *
- * @throws HttpErrorMessage if not
- */
- private void assertCurrentUserIsOwnerOfTheProject(Project project) {
- if (!securityManager.isCurrentUserOwnerOfTheProject(project)) {
- throw HttpErrorMessage.dataError(MessageI18nKey.DATA_INTEGRITY_FAILURE);
- }
- }
- /**
- * Delete the given team member
- *
- * @param teamMemberId the id of the team member
- */
- public void deleteTeamMember(Long teamMemberId) {
- TeamMember teamMember = assertAndGetMember(teamMemberId);
- if (!checkDeletionAcceptability(teamMember)) {
- throw HttpErrorMessage.dataError(MessageI18nKey.DATA_INTEGRITY_FAILURE);
- }
- // assignments deleted by cascade
- // delete invitation token
- tokenManager.deleteInvitationsByTeamMember(teamMember);
- if (teamMember.getProject() != null) {
- teamMember.getProject().getTeamMembers().remove(teamMember);
- }
- teamMemberDao.deleteTeamMember(teamMember);
- }
- /**
- * Ascertain that the team member can be deleted
- *
- * @param teamMember the team member to check for deletion
- *
- * @return True iff it can be safely deleted
- */
- public boolean checkDeletionAcceptability(TeamMember teamMember) {
- if (teamMember.getPosition() == HierarchicalPosition.OWNER &&
- teamMember.getProject().getTeamMembersByPosition(HierarchicalPosition.OWNER)
- .size() < 2) {
- return false;
- }
- return true;
- }
- // *********************************************************************************************
- // Invitations and sharing
- // *********************************************************************************************
- /**
- * Send invitation
- *
- * @param projectId id of the project
- * @param email send invitation to this address
- *
- * @return the pending new teamMember
- */
- public TeamMember invite(Long projectId, String email) {
- Project project = projectManager.assertAndGetProject(projectId);
- logger.debug("Invite {} to join {}", email, project);
- return tokenManager.sendMembershipInvitation(project, email);
- }
- /**
- * Create a token to share the project.
- *
- * @param projectId The id of the project that will become visible (mandatory)
- * @param cardId The id of the card that will become editable (optional)
- *
- * @return the URL to use to consume the token
- */
- public String generateSharingLinkToken(Long projectId, Long cardId) {
- Project project = projectManager.assertAndGetProject(projectId);
- Card card = cardManager.assertAndGetCard(cardId);
- logger.debug("Generate sharing link token for project {} and card {}", project, card);
- return tokenManager.generateSharingLinkToken(project, card);
- }
- /**
- * Delete all sharing link tokens for the given project.
- *
- * @param projectId the id of the project
- */
- public void deleteSharingLinkTokensByProject(Long projectId) {
- Project project = projectManager.assertAndGetProject(projectId);
- logger.debug("Delete sharing link token for project {}", projectId);
- tokenManager.deleteSharingLinkTokensByProject(project);
- }
- /**
- * Delete all sharing link tokens for the given card.
- *
- * @param cardId the id of the card
- */
- public void deleteSharingLinkTokensByCard(Long cardId) {
- Card card = cardManager.assertAndGetCard(cardId);
- logger.debug("Delete sharing link token for card {}", cardId);
- tokenManager.deleteSharingLinkTokensByCard(card);
- }
- // *********************************************************************************************
- // Roles
- // *********************************************************************************************
- /**
- * Retrieve the role. If not found, throw a {@link HttpErrorMessage}.
- *
- * @param roleId the id of the role
- *
- * @return the role if found
- *
- * @throws HttpErrorMessage if the role was not found
- */
- public TeamRole assertAndGetRole(Long roleId) {
- TeamRole role = teamRoleDao.findRole(roleId);
- if (role == null) {
- logger.error("team role #{} not found", roleId);
- throw HttpErrorMessage.dataError(MessageI18nKey.DATA_NOT_FOUND);
- }
- return role;
- }
- /**
- * Get all the roles defined in the given project
- *
- * @param id the project
- *
- * @return list of roles
- */
- public List<TeamRole> getProjectRoles(Long id) {
- Project project = projectManager.assertAndGetProject(id);
- return project.getRoles();
- }
- /**
- * Get the team roles defined in the given project
- *
- * @param projectId the id of the project
- *
- * @return list of team roles
- */
- public List<TeamRole> getTeamRolesForProject(Long projectId) {
- logger.debug("Get team roles of project #{}", projectId);
- Project project = projectManager.assertAndGetProject(projectId);
- return project.getRoles();
- }
- /**
- * Create a role. The role must have a projectId set.
- *
- * @param role role to create
- *
- * @return the brand new persisted role
- */
- public TeamRole createRole(TeamRole role) {
- if (role.getProjectId() != null) {
- Project project = projectManager.assertAndGetProject(role.getProjectId());
- if (project.getRoleByName(role.getName()) == null) {
- project.getRoles().add(role);
- role.setProject(project);
- return role;
- }
- }
- throw HttpErrorMessage.dataError(MessageI18nKey.DATA_INTEGRITY_FAILURE);
- }
- /**
- * Delete role
- *
- * @param roleId id of the role to delete
- */
- public void deleteRole(Long roleId) {
- TeamRole role = teamRoleDao.findRole(roleId);
- if (role != null) {
- Project project = role.getProject();
- if (project != null) {
- project.getRoles().remove(role);
- }
- role.getMembers().forEach(member -> {
- List<TeamRole> roles = member.getRoles();
- if (roles != null) {
- roles.remove(role);
- }
- });
- teamRoleDao.deleteRole(role);
- }
- }
- /**
- * Give a role to someone. The role and the member must belong to the very same project.
- *
- * @param roleId id of the role
- * @param memberId id of the teamMember
- */
- public void giveRole(Long roleId, Long memberId) {
- TeamRole role = teamRoleDao.findRole(roleId);
- TeamMember member = teamMemberDao.findTeamMember(memberId);
- if (role != null && member != null) {
- if (Objects.equals(role.getProject(), member.getProject())) {
- List<TeamMember> members = role.getMembers();
- List<TeamRole> roles = member.getRoles();
- if (!members.contains(member)) {
- members.add(member);
- }
- if (!roles.contains(role)) {
- roles.add(role);
- }
- } else {
- throw HttpErrorMessage.badRequest();
- }
- }
- }
- /**
- * Remove a role from someone.
- *
- * @param roleId id of the role
- * @param memberId id of the member
- */
- public void removeRole(Long roleId, Long memberId) {
- TeamRole role = teamRoleDao.findRole(roleId);
- TeamMember member = teamMemberDao.findTeamMember(memberId);
- if (role != null && member != null) {
- List<TeamMember> members = role.getMembers();
- List<TeamRole> roles = member.getRoles();
- members.remove(member);
- roles.remove(role);
- }
- }
- }