diff --git a/appcontext/app.go b/appcontext/app.go index 5fbd81e..c18d3ee 100644 --- a/appcontext/app.go +++ b/appcontext/app.go @@ -16,6 +16,7 @@ import ( "houston/pkg/google/googleDrive" "houston/pkg/postgres" "houston/pkg/rest" + "houston/repository/externalTeamRepo" rcaRepository "houston/repository/rca/impl" "houston/repository/rcaInput" teamSeverityRepo "houston/repository/teamSeverity" @@ -32,6 +33,7 @@ import ( severityServiceImpl "houston/service/severity/impl" "houston/service/slack" tagService "houston/service/tag" + "houston/service/teamService" "houston/service/teamSeverity" teamSeverityServiceImpl "houston/service/teamSeverity/impl" "houston/service/teamUser" @@ -68,6 +70,7 @@ type houstonServices struct { userService userService.UserService teamUserService teamUser.ITeamUserService teamUserSeverityService teamUserSeverity.ITeamUserSeverityService + teamService teamService.ITeamServiceV2 } var appContext *applicationContext @@ -106,6 +109,7 @@ func InitializeServices() { userService: initUserService(), teamUserService: initTeamUserService(), teamUserSeverityService: initTeamUserSeverityService(), + teamService: initTeamService(), } } @@ -178,6 +182,23 @@ func initCalendarService() *conference.CalendarService { return calendarService } +func initTeamService() teamService.ITeamServiceV2 { + return teamService.NewTeamServiceV2( + initTeamRepo(initLogRepo()), + initUserRepo(), + initSlackService(), + externalTeamRepo.NewExternalTeamRepository(GetDB()), + initTeamSeverityService(), + initUserService(), + initTeamUserService(), + initTeamUserSeverityService(), + ) +} + +func GetTeamService() teamService.ITeamServiceV2 { + return services.teamService +} + func initTagService() *tagService.TagService { return tagService.NewTagService(GetDB()) } diff --git a/cmd/app/handler/team_handler.go b/cmd/app/handler/team_handler.go index 07882fe..2a5bc24 100644 --- a/cmd/app/handler/team_handler.go +++ b/cmd/app/handler/team_handler.go @@ -51,6 +51,22 @@ func (handler *TeamHandler) HandleGetAllTeams(c *gin.Context) { c.JSON(http.StatusOK, common.SuccessResponse(teamResponses, http.StatusOK)) } +func (handler *TeamHandler) HandleGetTeam(c *gin.Context) { + teamId, err := validateGetTeamParams(c) + if err != nil { + handler.handleErrorResponse(c, err) + return + } + + teamResponse, err := handler.service.GetTeam(teamId) + if err != nil { + handler.handleErrorResponse(c, err) + return + } + + c.JSON(http.StatusOK, common.SuccessResponse(teamResponse, http.StatusOK)) +} + func (handler *TeamHandler) handleErrorResponse(c *gin.Context, err error) { var dataAccessError *customErrors.DataAccessError if errors.As(err, &dataAccessError) { @@ -95,6 +111,53 @@ func (handler *TeamHandler) HandleAddTeam(c *gin.Context) { c.JSON(http.StatusCreated, common.SuccessResponse(addTeamResponse, http.StatusCreated)) } +func (handler *TeamHandler) HandleUpdateTeam(c *gin.Context, updateTeamRequest team.UpdateTeamRequest) { + if err := validateUpdateTeamRequest(updateTeamRequest); err != nil { + handler.handleErrorResponse(c, err) + return + } + + err := handler.service.UpdateTeam(updateTeamRequest, c.GetHeader(util.UserEmailHeader)) + if err != nil { + handler.handleErrorResponse(c, err) + return + } + + c.JSON(http.StatusOK, common.SuccessResponse("Team Updated Successfully", http.StatusOK)) +} + +func (handler *TeamHandler) HandleRemoveMember(c *gin.Context) { + teamId, userId, err := validateRemoveMemberParams(c) + if err != nil { + handler.handleErrorResponse(c, err) + return + } + + err = handler.service.RemoveTeamMember(teamId, userId, c.GetHeader(util.UserEmailHeader)) + if err != nil { + handler.handleErrorResponse(c, err) + return + } + + c.JSON(http.StatusOK, common.SuccessResponse("Member removed successfully", http.StatusOK)) +} + +func (handler *TeamHandler) HandleMakeManager(c *gin.Context) { + teamId, memberToMakeManagerId, err := validateMakeManagerParams(c) + if err != nil { + handler.handleErrorResponse(c, err) + return + } + + err = handler.service.MakeManager(teamId, memberToMakeManagerId, c.GetHeader(util.UserEmailHeader)) + if err != nil { + handler.handleErrorResponse(c, err) + return + } + + c.JSON(http.StatusOK, common.SuccessResponse("Team manager updated successfully", http.StatusOK)) +} + func validateAddTeamRequest(request team.AddTeamRequest) error { teamName := request.Name minLength := viper.GetInt("TEAM_NAME_MIN_LENGTH") @@ -118,3 +181,63 @@ func validateAddTeamRequest(request team.AddTeamRequest) error { return nil } + +func validateUpdateTeamRequest(request team.UpdateTeamRequest) error { + if request.Id == 0 { + return customErrors.NewInvalidInputError("Team id not provided") + } + + if request.Members == nil && util.IsBlank(request.PseOnCallId) && + util.IsBlank(request.OnCallId) && util.IsBlank(request.SlackChannel) { + return customErrors.NewInvalidInputError("No fields provided to update") + } + + if request.Members != nil { + if request.Members.SeverityId == 0 { + return customErrors.NewInvalidInputError("Severity id not provided for members to add") + } + + if len(request.Members.UserEmailIds) == 0 { + return customErrors.NewInvalidInputError("No user email ids provided for members to add") + } + } + + return nil +} + +func validateRemoveMemberParams(c *gin.Context) (uint, uint, error) { + teamId, err := strconv.Atoi(c.Param(util.IdParam)) + if err != nil || teamId == 0 { + return 0, 0, customErrors.NewInvalidInputError("Invalid team id") + } + + userId, err := strconv.Atoi(c.Param(util.UserIdParam)) + if err != nil || userId == 0 { + return 0, 0, customErrors.NewInvalidInputError("Invalid user id") + } + + return uint(teamId), uint(userId), nil +} + +func validateMakeManagerParams(c *gin.Context) (uint, uint, error) { + teamId, err := strconv.Atoi(c.Param(util.IdParam)) + if err != nil || teamId == 0 { + return 0, 0, customErrors.NewInvalidInputError("Invalid team id") + } + + userId, err := strconv.Atoi(c.Param(util.UserIdParam)) + if err != nil || userId == 0 { + return 0, 0, customErrors.NewInvalidInputError("Invalid user id to make manager") + } + + return uint(teamId), uint(userId), nil +} + +func validateGetTeamParams(c *gin.Context) (uint, error) { + teamId, err := strconv.Atoi(c.Param(util.IdParam)) + if err != nil || teamId == 0 { + return 0, customErrors.NewInvalidInputError("Invalid team id") + } + + return uint(teamId), nil +} diff --git a/cmd/app/server.go b/cmd/app/server.go index 7a62557..66fef50 100644 --- a/cmd/app/server.go +++ b/cmd/app/server.go @@ -104,11 +104,15 @@ func (s *Server) teamHandler(houstonGroup *gin.RouterGroup) { houstonGroup.GET("/teams", teamHandler.GetTeams) houstonGroup.GET("/teams/:id", teamHandler.GetTeams) } + houstonGroup.GET("/teams-v2/:id", teamHandlerV2.HandleGetTeam) houstonGroup.POST("/teams/add", teamHandler.AddTeam) houstonGroup.POST("/teams-v2/add", s.authService.IfAdmin(teamHandlerV2.HandleAddTeam)) + houstonGroup.POST("/teams-v2", s.authService.IfMemberOrAdmin(teamHandlerV2.HandleUpdateTeam)) houstonGroup.POST("/teams", teamHandler.UpdateTeam) houstonGroup.PATCH("/teams/:id/manager/:userId", teamHandler.MakeManager) + houstonGroup.PATCH("/teams-v2/:id/manager/:userId", s.authService.IfManagerOrAdmin(teamHandlerV2.HandleMakeManager)) houstonGroup.DELETE("/teams/:id/members/:userId", teamHandler.RemoveTeamMember) + houstonGroup.DELETE("/teams-v2/:id/members/:userId", s.authService.IfManagerOrAdmin(teamHandlerV2.HandleRemoveMember)) houstonGroup.DELETE("/teams/:id", teamHandler.RemoveTeam) } diff --git a/common/util/constant.go b/common/util/constant.go index a2e8940..b0897a2 100644 --- a/common/util/constant.go +++ b/common/util/constant.go @@ -103,6 +103,7 @@ const ( const ( IdParam = "id" IncidentIdParam = "incidentId" + UserIdParam = "userId" ) const ( diff --git a/model/user/user.go b/model/user/user.go index 44265ab..434514c 100644 --- a/model/user/user.go +++ b/model/user/user.go @@ -106,6 +106,15 @@ func (r *Repository) GetHoustonUserByEmailId(emailId string) (*UserEntity, error return &user, nil } +func (r *Repository) GetHoustonUsersByEmailIds(emailIds []string) (*[]UserEntity, error) { + var users []UserEntity + result := r.gormClient.Where("email IN ?", emailIds).Find(&users) + if result.Error != nil { + return nil, result.Error + } + return &users, nil +} + func (r *Repository) GetHoustonUserById(id uint) (*UserEntity, error) { var user UserEntity result := r.gormClient.Where("id = ?", id).First(&user) diff --git a/repository/teamUser/team_user_repository_impl.go b/repository/teamUser/team_user_repository_impl.go index 9c5c067..d65bde4 100644 --- a/repository/teamUser/team_user_repository_impl.go +++ b/repository/teamUser/team_user_repository_impl.go @@ -56,3 +56,18 @@ func (repo *teamUserRepositoryImpl) RemoveTeamUserByTeamIdAndUserId(teamId, user return nil } + +func (repo *teamUserRepositoryImpl) GetTeamUserByTeamIdAndUserEmailId(teamId uint, userEmailId string) (*teamUser.TeamUserEntity, error) { + var teamUser teamUser.TeamUserEntity + if err := repo.gormClient. + Preload("User"). + Preload("Team"). + Joins("JOIN houston_user ON houston_user.id = team_user.user_id"). + Where("houston_user.email = ? AND team_user.team_id = ?", userEmailId, teamId). + First(&teamUser). + Error; err != nil { + return nil, err + } + + return &teamUser, nil +} diff --git a/repository/teamUser/team_user_repository_interface.go b/repository/teamUser/team_user_repository_interface.go index 7ffb8ed..6e1e6b9 100644 --- a/repository/teamUser/team_user_repository_interface.go +++ b/repository/teamUser/team_user_repository_interface.go @@ -11,6 +11,7 @@ type TeamUserRepository interface { GetTeamUserByTeamIdAndUserId(teamId, userId uint) (teamUser.TeamUserEntity, error) GetTeamsByUserId(userId uint) ([]teamUser.TeamUserEntity, error) RemoveTeamUserByTeamIdAndUserId(teamId, userId uint) error + GetTeamUserByTeamIdAndUserEmailId(teamId uint, userEmailId string) (*teamUser.TeamUserEntity, error) } func NewTeamUserRepository(gormClient *gorm.DB) TeamUserRepository { diff --git a/repository/teamUserSeverity/team_user_severity_repository_impl.go b/repository/teamUserSeverity/team_user_severity_repository_impl.go index ebcc479..f1d313c 100644 --- a/repository/teamUserSeverity/team_user_severity_repository_impl.go +++ b/repository/teamUserSeverity/team_user_severity_repository_impl.go @@ -18,9 +18,19 @@ func (repo *teamUserSeverityRepositoryImpl) AddTeamUserSeverity(teamUserSeverity return nil } -func (repo *teamUserSeverityRepositoryImpl) GetTeamUserSeveritiesByTeamUserId(teamUserId uint) ([]teamUserSeverity.TeamUserSeverityEntity, error) { - var teamUserSeverities []teamUserSeverity.TeamUserSeverityEntity - result := repo.gormClient.Where("team_user = ?", teamUserId).Preload("TeamUserEntity").Preload("TeamSeverityEntity").Find(&teamUserSeverities) +func (repo *teamUserSeverityRepositoryImpl) GetTeamUserSeverityByTeamUserId(teamUserId uint) (*teamUserSeverity.TeamUserSeverityEntity, error) { + var teamUserSeverity teamUserSeverity.TeamUserSeverityEntity + result := repo.gormClient.Where("team_user = ?", teamUserId).Preload("TeamUserEntity").Preload("TeamSeverityEntity").First(&teamUserSeverity) + if result.Error != nil { + return nil, result.Error + } + + return &teamUserSeverity, nil +} + +func (repo *teamUserSeverityRepositoryImpl) GetTeamUserSeveritiesByTeamSeverityId(teamSeverityId uint) ([]*teamUserSeverity.TeamUserSeverityEntity, error) { + var teamUserSeverities []*teamUserSeverity.TeamUserSeverityEntity + result := repo.gormClient.Where("team_severity = ?", teamSeverityId).Preload("TeamUserEntity").Preload("TeamSeverityEntity").Find(&teamUserSeverities) if result.Error != nil { return nil, result.Error } @@ -37,11 +47,17 @@ func (repo *teamUserSeverityRepositoryImpl) UpdateTeamUserSeverity(teamUserSever return nil } -func (repo *teamUserSeverityRepositoryImpl) RemoveTeamUserSeverityByTeamIdAndUserId(teamId, userId uint) error { - result := repo.gormClient.Where("team_id = ? AND user_id = ?", teamId, userId).Delete(&teamUserSeverity.TeamUserSeverityEntity{}) +func (repo *teamUserSeverityRepositoryImpl) RemoveTeamUserSeverityByTeamUserId(teamUserId uint) error { + result := repo.gormClient.Where("team_user = ?", teamUserId).Delete(&teamUserSeverity.TeamUserSeverityEntity{}) if result.Error != nil { return result.Error } return nil } + +func (repo *teamUserSeverityRepositoryImpl) UpdateTeamSeverityForTeamUser(teamUserId, teamSeverityId uint) error { + result := repo.gormClient.Model(&teamUserSeverity.TeamUserSeverityEntity{}).Where("team_user = ?", teamUserId).Update("team_severity", teamSeverityId) + + return result.Error +} diff --git a/repository/teamUserSeverity/team_user_severity_repository_interface.go b/repository/teamUserSeverity/team_user_severity_repository_interface.go index 33bf618..aa9083f 100644 --- a/repository/teamUserSeverity/team_user_severity_repository_interface.go +++ b/repository/teamUserSeverity/team_user_severity_repository_interface.go @@ -7,9 +7,10 @@ import ( type TeamUserSeverityRepository interface { AddTeamUserSeverity(teamUserSeverity teamUserSeverity.TeamUserSeverityEntity) error - GetTeamUserSeveritiesByTeamUserId(teamUserId uint) ([]teamUserSeverity.TeamUserSeverityEntity, error) UpdateTeamUserSeverity(teamUserSeverity teamUserSeverity.TeamUserSeverityEntity) error - RemoveTeamUserSeverityByTeamIdAndUserId(teamId, userId uint) error + GetTeamUserSeveritiesByTeamSeverityId(teamSeverityId uint) ([]*teamUserSeverity.TeamUserSeverityEntity, error) + RemoveTeamUserSeverityByTeamUserId(teamUserId uint) error + UpdateTeamSeverityForTeamUser(teamUserId, teamSeverityId uint) error } func NewTeamUserSeverityRepository(gormClient *gorm.DB) TeamUserSeverityRepository { diff --git a/service/auth_service.go b/service/auth_service.go index a2d39a6..1744153 100644 --- a/service/auth_service.go +++ b/service/auth_service.go @@ -4,22 +4,34 @@ import ( "fmt" "github.com/gin-gonic/gin" "go.uber.org/zap" + "houston/appcontext" + "houston/common/util" "houston/internal/clients" "houston/logger" + clientsResponse "houston/model/clients" "houston/pkg/slackbot" + "houston/service/request/team" + common "houston/service/response/common" + "houston/service/teamUser" + "houston/service/user" "net/http" + "strconv" "strings" ) type AuthService struct { - mjolnirClient *clients.MjolnirClient - slackClient *slackbot.Client + mjolnirClient *clients.MjolnirClient + slackClient *slackbot.Client + userService user.UserService + teamUserService teamUser.ITeamUserService } func NewAuthService(mjolnirClient *clients.MjolnirClient, slackClient *slackbot.Client) *AuthService { return &AuthService{ - mjolnirClient: mjolnirClient, - slackClient: slackClient, + mjolnirClient: mjolnirClient, + slackClient: slackClient, + userService: appcontext.GetUserService(), + teamUserService: appcontext.GetTeamUserService(), } } @@ -35,15 +47,14 @@ func (authService *AuthService) checkIfManagerOrAdmin( managerSlackHandle string, roles ...UserRoles, ) (bool, error) { - sessionToken := context.Request.Header.Get("X-Session-Token") - userEmail := context.Request.Header.Get("X-User-Email") - sessionResponse, err := authService.mjolnirClient.GetSessionResponse(sessionToken) + headerData := getSessionTokenAndUserEmail(context) + sessionResponse, err := authService.mjolnirClient.GetSessionResponse(headerData.sessionToken) if err != nil || sessionResponse.StatusCode == 401 { - logger.Error(fmt.Sprintf("error occurred while getting session data from mjolnir for %v", userEmail), zap.Error(err)) + logger.Error(fmt.Sprintf("error occurred while getting session data from mjolnir for %v", headerData.userEmail), zap.Error(err)) return false, nil } - if sessionResponse.EmailId != userEmail { - logger.Error(fmt.Sprintf("user email: %v does not match the email linked to the session token", userEmail)) + if sessionResponse.EmailId != headerData.userEmail { + logger.Error(fmt.Sprintf("user email: %v does not match the email linked to the session token", headerData.userEmail)) return false, nil } userRoles := strings.Join(sessionResponse.Roles, ",") @@ -53,9 +64,9 @@ func (authService *AuthService) checkIfManagerOrAdmin( expectedRoles += string(role) + "," } if strings.Contains(expectedRoles, "Manager") { - userInfo, err := authService.slackClient.GetUserByEmail(userEmail) + userInfo, err := authService.slackClient.GetUserByEmail(headerData.userEmail) if err != nil { - logger.Error(fmt.Sprintf("User with email %v was not found in slack", userEmail), zap.Error(err)) + logger.Error(fmt.Sprintf("User with email %v was not found in slack", headerData.userEmail), zap.Error(err)) return false, nil } if userInfo.ID == managerSlackHandle { @@ -65,48 +76,152 @@ func (authService *AuthService) checkIfManagerOrAdmin( return strings.Contains(userRoles, string(Admin)), nil } +func (authService *AuthService) isAdmin(sessionResponse *clientsResponse.MjolnirSessionResponse) bool { + if !strings.Contains(strings.Join(sessionResponse.Roles, ","), string(Admin)) { + logger.Error(fmt.Sprintf("user is not an admin. Roles are: %s", sessionResponse.Roles)) + return false + } + + return true +} + +func (authService *AuthService) validateUserAndGetSessionResponse(sessionToken, emailID string) (*clientsResponse.MjolnirSessionResponse, error) { + sessionResponse, err := authService.mjolnirClient.GetSessionResponse(sessionToken) + + if err != nil || sessionResponse.StatusCode == http.StatusUnauthorized { + logger.Error(fmt.Sprintf("error occurred while getting session data from mjolnir for %v", sessionToken), zap.Error(err)) + return nil, err + } + + if sessionResponse.EmailId != emailID { + logger.Error(fmt.Sprintf("user email: %v does not match the email linked to the session token", emailID)) + return nil, fmt.Errorf("user email: %v does not match the email linked to the session token", emailID) + } + + return sessionResponse, nil +} + +func (authService *AuthService) isManager(teamId uint, userEmail string) bool { + teamUser, err := authService.teamUserService.GetTeamUserByTeamIdAndUserEmailId(teamId, userEmail) + if err != nil { + logger.Error(fmt.Sprintf("error occurred while getting team user with team id: %d and email: %s", teamId, userEmail), zap.Error(err)) + return false + } + if teamUser == nil { + logger.Info(fmt.Sprintf("user: %s is not a member of team: %d", userEmail, teamId)) + return false + } + + if teamUser.User.SlackUserId != teamUser.Team.ManagerHandle { + logger.Info(fmt.Sprintf("user: %s is not the manager of team: %d", userEmail, teamId)) + return false + } + + return true +} + +func (authService *AuthService) isMember(teamId uint, userEmail string) bool { + teamUser, err := authService.teamUserService.GetTeamUserByTeamIdAndUserEmailId(teamId, userEmail) + if err != nil { + logger.Error("error in fetching team users", zap.Error(err)) + return false + } + + if teamUser != nil { + return true + } + + return false +} + func (authService *AuthService) IfAdmin(fn gin.HandlerFunc) gin.HandlerFunc { return func(c *gin.Context) { - sessionToken := c.Request.Header.Get("X-Session-Token") - userEmail := c.Request.Header.Get("X-User-Email") - sessionResponse, err := authService.mjolnirClient.GetSessionResponse(sessionToken) - if err != nil || sessionResponse.StatusCode == http.StatusUnauthorized { - logger.Error(fmt.Sprintf("error occurred while getting session data from mjolnir for %v", userEmail), zap.Error(err)) + headerData := getSessionTokenAndUserEmail(c) + sessionResponse, err := authService.validateUserAndGetSessionResponse(headerData.sessionToken, headerData.userEmail) + if err != nil || sessionResponse == nil { c.AbortWithStatus(http.StatusUnauthorized) return } - if sessionResponse.EmailId != userEmail { - logger.Error(fmt.Sprintf("user email: %v does not match the email linked to the session token", userEmail)) - c.AbortWithStatus(http.StatusUnauthorized) - return - } - if !strings.Contains(strings.Join(sessionResponse.Roles, ","), string(Admin)) { - logger.Error(fmt.Sprintf("user: %v is not an admin. Roles are: %s", userEmail, sessionResponse.Roles)) + + if !authService.isAdmin(sessionResponse) { c.AbortWithStatus(http.StatusUnauthorized) return } + fn(c) } } +func (authService *AuthService) IfManagerOrAdmin(fn gin.HandlerFunc) gin.HandlerFunc { + return func(c *gin.Context) { + headerData := getSessionTokenAndUserEmail(c) + + sessionResponse, err := authService.validateUserAndGetSessionResponse(headerData.sessionToken, headerData.userEmail) + if err != nil || sessionResponse == nil { + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + teamId, err := strconv.Atoi(c.Param(util.IdParam)) + if err != nil { + logger.Error("error occurred while parsing team id", zap.Error(err)) + c.AbortWithStatus(http.StatusBadRequest) + return + } + + if !authService.isAdmin(sessionResponse) && !authService.isManager(uint(teamId), headerData.userEmail) { + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + fn(c) + } +} + +func (authService *AuthService) IfMemberOrAdmin(fn func(*gin.Context, team.UpdateTeamRequest)) gin.HandlerFunc { + return func(c *gin.Context) { + headerData := getSessionTokenAndUserEmail(c) + + sessionResponse, err := authService.validateUserAndGetSessionResponse(headerData.sessionToken, headerData.userEmail) + if err != nil || sessionResponse == nil { + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + var updateTeamRequest team.UpdateTeamRequest + + if err := c.ShouldBindJSON(&updateTeamRequest); err != nil { + c.JSON(http.StatusBadRequest, common.ErrorResponse(err, http.StatusBadRequest, nil)) + return + } + + if !authService.isAdmin(sessionResponse) && !authService.isMember(updateTeamRequest.Id, headerData.userEmail) { + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + //sending request object as a parameter to avoid EOF + fn(c, updateTeamRequest) + } +} + func (authService *AuthService) checkIfAdminOrTeamMember(context *gin.Context, teamMembers []string) (bool, error) { - sessionToken := context.Request.Header.Get("X-Session-Token") - userEmail := context.Request.Header.Get("X-User-Email") - sessionResponse, err := authService.mjolnirClient.GetSessionResponse(sessionToken) + headerData := getSessionTokenAndUserEmail(context) + sessionResponse, err := authService.mjolnirClient.GetSessionResponse(headerData.sessionToken) if err != nil || sessionResponse.StatusCode == 401 { - logger.Error(fmt.Sprintf("error occurred while getting session data from mjolnir for %v", userEmail), zap.Error(err)) + logger.Error(fmt.Sprintf("error occurred while getting session data from mjolnir for %v", headerData.userEmail), zap.Error(err)) return false, nil } - if sessionResponse.EmailId != userEmail { - logger.Error(fmt.Sprintf("user email: %v does not match the email linked to the session token", userEmail)) + if sessionResponse.EmailId != headerData.userEmail { + logger.Error(fmt.Sprintf("user email: %v does not match the email linked to the session token", headerData.userEmail)) return false, nil } if teamMembers == nil || len(teamMembers) == 0 { return strings.Contains(strings.Join(sessionResponse.Roles, ","), string(Admin)), nil } - userInfo, err := authService.slackClient.GetUserByEmail(userEmail) + userInfo, err := authService.slackClient.GetUserByEmail(headerData.userEmail) if err != nil { - logger.Error(fmt.Sprintf("User with email %v was not found in slack", userEmail), zap.Error(err)) + logger.Error(fmt.Sprintf("User with email %v was not found in slack", headerData.userEmail), zap.Error(err)) return false, nil } return strings.Contains(strings.Join(sessionResponse.Roles, ","), string(Admin)) || @@ -125,3 +240,15 @@ func (authService *AuthService) CheckValidUser(sessionToken, userEmail string) ( } return true, nil } + +func getSessionTokenAndUserEmail(context *gin.Context) *headersData { + return &headersData{ + sessionToken: context.Request.Header.Get(util.SessionTokenHeader), + userEmail: context.Request.Header.Get(util.UserEmailHeader), + } +} + +type headersData struct { + sessionToken string + userEmail string +} diff --git a/service/dtoConverter/team_dto_converter.go b/service/dtoConverter/team_dto_converter.go index c4b04ca..74682be 100644 --- a/service/dtoConverter/team_dto_converter.go +++ b/service/dtoConverter/team_dto_converter.go @@ -23,7 +23,7 @@ func TeamUserEntitiesToDTOs(entities []teamUser.TeamUserEntity) []*teamUser.Team return dtos } -func TeamUserSeverityEntitiesToDTOs(entities []teamUserSeverity.TeamUserSeverityEntity) []teamUserSeverity.TeamUserSeverityDTO { +func TeamUserSeverityEntitiesToDTOs(entities []*teamUserSeverity.TeamUserSeverityEntity) []teamUserSeverity.TeamUserSeverityDTO { var dtos []teamUserSeverity.TeamUserSeverityDTO for _, entity := range entities { dtos = append(dtos, entity.ToDTO()) diff --git a/service/request/team/update_team.go b/service/request/team/update_team.go new file mode 100644 index 0000000..11a75d1 --- /dev/null +++ b/service/request/team/update_team.go @@ -0,0 +1,14 @@ +package team + +type UpdateTeamRequest struct { + Id uint `json:"id"` + SlackChannel string `json:"slack_channel,omitempty"` + PseOnCallId string `json:"pse_on_call_id,omitempty"` + OnCallId string `json:"on_call_id,omitempty"` + Members *Members `json:"members,omitempty"` +} + +type Members struct { + SeverityId uint `json:"severity_id"` + UserEmailIds []string `json:"user_email_ids"` +} diff --git a/service/response/team_response_v2.go b/service/response/team_response_v2.go new file mode 100644 index 0000000..3d33fd6 --- /dev/null +++ b/service/response/team_response_v2.go @@ -0,0 +1,32 @@ +package service + +import "time" + +type TeamResponseV2 struct { + ID uint `json:"id"` + Name string `json:"name"` + UpdatedAt time.Time `json:"updated_at,omitempty"` + SlackChannel SlackChannel `json:"slack_channel"` + OnCall BotResponse `json:"oncall"` + PseOnCall BotResponse `json:"pse_oncall"` + SeverityMembersMap map[uint][]User `json:"severity_members_map,omitempty"` + ManagerDetails ManagerDetails `json:"manager_details"` +} + +type SlackChannel struct { + Id string `json:"id"` + Name string `json:"name"` +} + +type User struct { + Id uint `json:"id"` + Email string `json:"email"` + Name string `json:"name"` +} + +type ManagerDetails struct { + Id uint `json:"id"` + Email string `json:"email"` + Name string `json:"name"` + SeverityId uint `json:"severity_id"` +} diff --git a/service/teamService/team_service_v2.go b/service/teamService/team_service_v2.go index eec9527..fd181c8 100644 --- a/service/teamService/team_service_v2.go +++ b/service/teamService/team_service_v2.go @@ -10,7 +10,10 @@ import ( "houston/logger" "houston/model/customErrors" externalTeam "houston/model/externalTeam" + "houston/model/incident" "houston/model/team" + teamSeverityModel "houston/model/teamSeverity" + teamUserModel "houston/model/teamUser" "houston/model/user" "houston/repository/externalTeamRepo" teamRequest "houston/service/request/team" @@ -122,6 +125,7 @@ func (teamService *TeamServiceV2) GetAllTeams() ([]service.TeamResponse, error) func (teamService *TeamServiceV2) getTeamBotDetails(slackUserId string) (*service.BotResponse, error) { bot, err := teamService.userRepository.FindHoustonUserBySlackUserId(slackUserId) if err != nil { + logger.Error(fmt.Sprintf("%s error in fetching bot by slack user id: %s", logTag, slackUserId), zap.Error(err)) return nil, err } if bot == nil { @@ -133,6 +137,21 @@ func (teamService *TeamServiceV2) getTeamBotDetails(slackUserId string) (*servic }, nil } +func (teamService *TeamServiceV2) getTeamChannelDetails(channelId string) service.SlackChannel { + channel, err := teamService.slackService.GetConversationInfo(channelId) + if err != nil { + return service.SlackChannel{ + Id: channelId, + Name: util.NotFoundError, + } + } + + return service.SlackChannel{ + Id: channelId, + Name: channel.Name, + } +} + func (teamService *TeamServiceV2) getTeamUserResponses(users []user.UserEntity, slackUserIds []string) []service.UserResponse { existingSlackUserIdsMap := make(map[string]string) var userResponses []service.UserResponse @@ -211,6 +230,122 @@ func (teamService *TeamServiceV2) GetExternalTeam(teamId uint, provider string) return &externalTeamDTO, nil } +func (teamService *TeamServiceV2) GetTeam(teamId uint) (*service.TeamResponseV2, error) { + teamEntity, err := teamService.GetTeamById(teamId) + if err != nil { + return nil, err + } + + teamUsers, err := teamService.teamUserService.GetTeamUsersByTeamId(teamId) + if err != nil { + logger.Error(fmt.Sprintf("%s error in fetching team users for team with id: %d : %+v", logTag, teamId, err)) + return nil, customErrors.NewDataAccessError("Could not fetch team users for team") + } + + teamSeveritiesMap, err := teamService.teamSeverityService.GetSeveritiesMapForATeam(teamId) + if err != nil { + logger.Error(fmt.Sprintf("%s error in fetching severities for team with id: %d : %+v", logTag, teamId, err)) + return nil, customErrors.NewDataAccessError("Could not fetch severities for team") + } + + teamResponse := service.TeamResponseV2{ + ID: teamEntity.ID, + Name: teamEntity.Name, + UpdatedAt: teamEntity.UpdatedAt, + } + + err = teamService.fillParticipantDetails(teamEntity, &teamResponse, teamSeveritiesMap, teamUsers) + if err != nil { + return nil, err + } + + teamResponse.SeverityMembersMap = teamService.sortParticipantGroups(teamResponse.SeverityMembersMap) + + onCallDetails, _ := teamService.getTeamBotDetails(teamEntity.OncallHandle) + if onCallDetails != nil { + teamResponse.OnCall = *onCallDetails + } + + pseOnCallDetails, _ := teamService.getTeamBotDetails(teamEntity.PseOncallHandle) + if pseOnCallDetails != nil { + teamResponse.PseOnCall = *pseOnCallDetails + } + + teamResponse.SlackChannel = teamService.getTeamChannelDetails(teamEntity.WebhookSlackChannel) + + return &teamResponse, nil +} + +func findUserByTeamUserId(teamUsers []*teamUserModel.TeamUserDTO, teamUserId uint) *teamUserModel.TeamUserDTO { + for _, teamUser := range teamUsers { + if teamUser.ID == teamUserId { + return teamUser + } + } + return nil +} + +func (teamService *TeamServiceV2) fillParticipantDetails( + teamEntity *team.TeamEntity, + teamResponse *service.TeamResponseV2, + teamSeveritiesMap map[uint]teamSeverityModel.TeamSeverityDTO, + teamUsers []*teamUserModel.TeamUserDTO, +) error { + teamResponse.SeverityMembersMap = make(map[uint][]service.User) + for sevId := incident.Sev0Id; sevId <= incident.Sev3Id; sevId++ { + teamResponse.SeverityMembersMap[sevId] = make([]service.User, 0) + teamSeverity := teamSeveritiesMap[sevId] + teamUserSeverities, err := teamService.teamUserSeverityService.GetTeamUserSeveritiesByTeamSeverityId(teamSeverity.ID) + if err != nil { + logger.Error(fmt.Sprintf("%s error in fetching team user severities for team severity with id: %d : %+v", logTag, teamSeverity.ID, err)) + return customErrors.NewDataAccessError("Could not fetch team user severities for team severity") + } + + if len(teamUserSeverities) > 0 { + for _, teamUserSeverity := range teamUserSeverities { + teamUserData := findUserByTeamUserId(teamUsers, teamUserSeverity.TeamUser) + if teamUserData != nil { + teamService.addUserDataToResponse(teamUserData, teamEntity, teamResponse, sevId) + } else { + logger.Error(fmt.Sprintf("%s team user not found for id: %d", logTag, teamUserSeverity.TeamUser)) + return customErrors.NewDataAccessError(fmt.Sprintf("team user not found for id: %d", teamUserSeverity.TeamUser)) + } + } + } + } + return nil +} + +func (teamService *TeamServiceV2) addUserDataToResponse( + teamUserData *teamUserModel.TeamUserDTO, teamEntity *team.TeamEntity, teamResponse *service.TeamResponseV2, severityId uint, +) { + if teamUserData.User.SlackUserId != teamEntity.ManagerHandle { + teamResponse.SeverityMembersMap[severityId] = append(teamResponse.SeverityMembersMap[severityId], + service.User{ + Id: teamUserData.User.ID, + Email: teamUserData.User.Email, + Name: teamUserData.User.RealName, + }) + } else { + teamResponse.ManagerDetails = service.ManagerDetails{ + Id: teamUserData.User.ID, + Email: teamUserData.User.Email, + Name: teamUserData.User.RealName, + SeverityId: severityId, + } + } +} + +func (teamService *TeamServiceV2) sortParticipantGroups(severityMembersMap map[uint][]service.User) map[uint][]service.User { + for _, group := range severityMembersMap { + sort.SliceStable(group, func(i, j int) bool { + return group[i].Name < group[j].Name + }) + } + + return severityMembersMap +} + func (teamService *TeamServiceV2) AddTeam(request teamRequest.AddTeamRequest, userEmail string) (*service.AddTeamResponse, error) { managerEmail := *request.ManagerDetails.Email managerSeverityId := *request.ManagerDetails.SeverityId @@ -276,3 +411,318 @@ func (teamService *TeamServiceV2) AddTeam(request teamRequest.AddTeamRequest, us Message: "Team created successfully!", }, nil } + +func (teamService *TeamServiceV2) UpdateTeam(request teamRequest.UpdateTeamRequest, userEmail string) error { + teamEntity, err := teamService.GetTeamById(request.Id) + if err != nil { + return err + } + + if err := teamService.updateChannelAndOnCallDetails(teamEntity, request); err != nil { + return err + } + + if err := teamService.addMembers(teamEntity, request.Members); err != nil { + return err + } + + teamEntity.UpdatedBy = userEmail + err = teamService.teamRepository.UpdateTeam(teamEntity) + if err != nil { + logger.Error(fmt.Sprintf("%s could not update team with id: %d : %+v", logTag, teamEntity.ID, err)) + return customErrors.NewDataAccessError(fmt.Sprintf("could not update team with id: %d", teamEntity.ID)) + } + + return nil +} + +func (teamService *TeamServiceV2) RemoveTeamMember(teamId, userId uint, requesterEmail string) error { + teamEntity, err := teamService.GetTeamById(teamId) + if err != nil { + return err + } + + user, err := teamService.userService.GetHoustonUserById(userId) + if err != nil { + logger.Error(fmt.Sprintf("%s error in fetching user by id: %d : %+v", logTag, userId, err)) + return customErrors.NewDataAccessError(fmt.Sprintf("error in fetching user with id: %d", userId)) + } + if user == nil { + logger.Error(fmt.Sprintf("%s user not found for id: %d", logTag, userId)) + return customErrors.NewNotFoundError(fmt.Sprintf("team user not found for id: %d", userId)) + } + + if user.SlackUserId == teamEntity.ManagerHandle { + return customErrors.NewInvalidInputError("Manager cannot be removed from the team") + } + + err = teamService.removeUserSpecificMappingsFromTeam(teamId, userId) + if err != nil { + return err + } + + err = teamService.removeUserFromTeamEntity(*teamEntity, user.SlackUserId, requesterEmail) + if err != nil { + return err + } + + return nil +} + +func (teamService *TeamServiceV2) MakeManager(teamId, memberToMakeManagerId uint, requesterEmail string) error { + teamEntity, err := teamService.GetTeamById(teamId) + if err != nil { + return err + } + + user, err := teamService.teamUserService.GetTeamUserByTeamIdAndUserId(teamId, memberToMakeManagerId) + if err != nil { + logger.Error(fmt.Sprintf("%s could not fetch team user by teamId: %d, userId: %d : %+v", logTag, teamId, memberToMakeManagerId, err)) + return customErrors.NewNotFoundError(fmt.Sprintf("could not fetch team user by teamId: %d, userId: %d", teamId, memberToMakeManagerId)) + } + + teamEntity.ManagerHandle = user.User.SlackUserId + teamEntity.UpdatedBy = requesterEmail + err = teamService.teamRepository.UpdateTeam(teamEntity) + if err != nil { + logger.Error(fmt.Sprintf("%s could not update team with id: %d : %+v", logTag, teamEntity.ID, err)) + return customErrors.NewDataAccessError(fmt.Sprintf("could not update team manager with id: %d", teamEntity.ID)) + } + + return nil +} + +func (teamService *TeamServiceV2) GetTeamById(teamId uint) (*team.TeamEntity, error) { + teamEntity, err := teamService.teamRepository.FindTeamById(teamId) + if err != nil { + logger.Error(fmt.Sprintf("%s error in fetching team by teamId: %d : %+v", logTag, teamId, err)) + return nil, customErrors.NewDataAccessError(fmt.Sprintf("error in fetching team with id: %d", teamId)) + } + + if teamEntity == nil { + logger.Error(fmt.Sprintf("%s team not found for teamId: %d", logTag, teamId)) + return nil, customErrors.NewNotFoundError(fmt.Sprintf("team not found for teamId: %d", teamId)) + } + + if !teamEntity.Active { + logger.Error(fmt.Sprintf("%s team is inactive for teamId: %d", logTag, teamId)) + return nil, customErrors.NewNotFoundError(fmt.Sprintf("team is inactive for teamId: %d", teamId)) + } + + return teamEntity, nil +} + +func (teamService *TeamServiceV2) updateChannelAndOnCallDetails(teamEntity *team.TeamEntity, request teamRequest.UpdateTeamRequest) error { + if err := teamService.updateSlackChannel(teamEntity, request.SlackChannel); err != nil { + return err + } + + if err := teamService.updateOnCallHandle(teamEntity, request.OnCallId); err != nil { + return err + } + + if err := teamService.updatePseOnCallHandle(teamEntity, request.PseOnCallId); err != nil { + return err + } + + return nil +} + +func (teamService *TeamServiceV2) updateSlackChannel(teamEntity *team.TeamEntity, channelId string) error { + if util.IsBlank(channelId) { + return nil + } + + _, err := teamService.slackService.GetConversationInfo(channelId) + if err != nil { + logger.Error(fmt.Sprintf("%s error in fetching channel info for channel: %s : %+v", logTag, channelId, err)) + return customErrors.NewInvalidInputError("Unable to find Slack channel with the provided I.D. Please verify the I.D. and re-submit.") + } + + teamEntity.WebhookSlackChannel = channelId + + return nil +} + +func (teamService *TeamServiceV2) getHoustonUserDataBySlackUserId(slackUserId string) (*user.UserDTO, error) { + userData, err := teamService.userService.GetHoustonUserBySlackUserId(slackUserId) + if err != nil { + logger.Error(fmt.Sprintf("%s error in fetching user by slack user id: %s : %+v", logTag, slackUserId, err)) + return nil, customErrors.NewInvalidInputError(fmt.Sprintf("Cannot find user with id: %v", slackUserId)) + } + + if userData == nil { + logger.Error(fmt.Sprintf("%s user not found for slack user id: %s", logTag, slackUserId)) + return nil, customErrors.NewNotFoundError(fmt.Sprintf("User not found for slack user id: %s", slackUserId)) + } + + return userData, nil +} + +func (teamService *TeamServiceV2) updateOnCallHandle(teamEntity *team.TeamEntity, onCallHandle string) error { + if util.IsBlank(onCallHandle) { + return nil + } + + userData, err := teamService.getHoustonUserDataBySlackUserId(onCallHandle) + if err != nil { + return err + } + + teamEntity.OncallHandle = userData.SlackUserId + + return nil +} + +func (teamService *TeamServiceV2) updatePseOnCallHandle(teamEntity *team.TeamEntity, pseOnCallHandle string) error { + if util.IsBlank(pseOnCallHandle) { + return nil + } + + userData, err := teamService.getHoustonUserDataBySlackUserId(pseOnCallHandle) + if err != nil { + return err + } + + teamEntity.PseOncallHandle = userData.SlackUserId + + return nil +} + +func (teamService *TeamServiceV2) addMembers(teamEntity *team.TeamEntity, members *teamRequest.Members) error { + if members == nil { + return nil + } + + severityId := members.SeverityId + userEmailIds := members.UserEmailIds + + teamSeverityMap, err := teamService.teamSeverityService.GetSeveritiesMapForATeam(teamEntity.ID) + if err != nil { + logger.Error(fmt.Sprintf("%s error in fetching severities for team with id: %d : %+v", logTag, teamEntity.ID, err)) + return customErrors.NewDataAccessError("Could not fetch severities for team") + } + + teamUsers, err := teamService.teamUserService.GetTeamUsersByTeamId(teamEntity.ID) + if err != nil { + logger.Error(fmt.Sprintf("%s error in fetching team users for team with id: %d : %+v", logTag, teamEntity.ID, err)) + return customErrors.NewDataAccessError("Could not fetch team users for team") + } + + for _, requestedUserEmail := range userEmailIds { + err := teamService.processUserAddition(teamEntity, teamUsers, teamSeverityMap, requestedUserEmail, severityId) + if err != nil { + return err + } + } + + //todo: remove slack user ids column in long term plan + teamEntity.SlackUserIds = util.RemoveDuplicate(teamEntity.SlackUserIds) + + return nil +} + +func (teamService *TeamServiceV2) processUserAddition( + teamEntity *team.TeamEntity, + teamUsers []*teamUserModel.TeamUserDTO, + teamSeverityMap map[uint]teamSeverityModel.TeamSeverityDTO, + requestedUserEmail string, + severityId uint, +) error { + teamUser := findUserInTeam(requestedUserEmail, teamUsers) + + if teamUser == nil { + return teamService.createAllMappingsForNewMember(requestedUserEmail, teamEntity, teamSeverityMap[severityId].ID) + } + + return teamService.updateTeamSeverityMappingForExistingMember(teamUser.ID, teamSeverityMap[severityId].ID) +} + +func (teamService *TeamServiceV2) createAllMappingsForNewMember( + requestedUserEmail string, teamEntity *team.TeamEntity, teamSeverityId uint, +) error { + user, err := teamService.userService.GetHoustonUserByEmailId(requestedUserEmail) + if err != nil { + logger.Error(fmt.Sprintf("%s error in fetching user by email: %s : %+v", logTag, requestedUserEmail, err)) + return customErrors.NewInvalidInputError(fmt.Sprintf("Cannot find user with email: %v", requestedUserEmail)) + } + if user == nil { + logger.Error(fmt.Sprintf("%s user not found for email: %s", logTag, requestedUserEmail)) + return customErrors.NewNotFoundError(fmt.Sprintf("User not found for email: %s", requestedUserEmail)) + } + + teamUserEntry, err := teamService.teamUserService.AddTeamUser(teamEntity.ID, user.ID) + if err != nil { + logger.Error(fmt.Sprintf("%s error in adding user to team with id: %d : %+v", logTag, teamEntity.ID, err)) + return customErrors.NewDataAccessError("Could not add user to team") + } + + err = teamService.teamUserSeverityService.AddTeamUserSeverity(teamUserEntry.ID, teamSeverityId) + if err != nil { + logger.Error(fmt.Sprintf("%s error in adding user to team severity with id: %d : %+v", logTag, teamSeverityId, err)) + return customErrors.NewDataAccessError("Could not add user to severity") + } + + //todo: remove slack user ids column in long term plan + teamEntity.SlackUserIds = append(teamEntity.SlackUserIds, user.SlackUserId) + + return nil +} + +func (teamService *TeamServiceV2) updateTeamSeverityMappingForExistingMember(teamUserId, teamSeverityId uint) error { + err := teamService.teamUserSeverityService.UpdateTeamSeverityForTeamUser(teamUserId, teamSeverityId) + if err != nil { + logger.Error(fmt.Sprintf("%s error in updating severity for user with id: %d : %+v", logTag, teamUserId, err)) + return customErrors.NewDataAccessError("Could not update severity for user") + } + + return nil +} + +func (teamService *TeamServiceV2) removeUserSpecificMappingsFromTeam(teamId, userId uint) error { + teamUserData, err := teamService.teamUserService.GetTeamUserByTeamIdAndUserId(teamId, userId) + if err != nil { + logger.Error(fmt.Sprintf("%s error in fetching team user by teamId: %d, userId: %d : %+v", logTag, teamId, userId, err)) + return customErrors.NewDataAccessError(fmt.Sprintf("error in fetching team user with teamId: %d, userId: %d", teamId, userId)) + } + + err = teamService.teamUserSeverityService.RemoveTeamUserSeverityByTeamUserId(teamUserData.ID) + if err != nil { + logger.Error(fmt.Sprintf("%s error in removing team user severity by teamUserId: %d : %+v", logTag, teamUserData.ID, err)) + return customErrors.NewDataAccessError("Could not remove member from team") + } + + err = teamService.teamUserService.RemoveTeamUser(teamId, userId) + if err != nil { + logger.Error(fmt.Sprintf("%s error in removing team user by teamId: %d, userId: %d : %+v", logTag, teamId, userId, err)) + return customErrors.NewDataAccessError("Could not remove member from team") + } + + return nil +} + +// todo: delete this function while removing slack user ids column +func (teamService *TeamServiceV2) removeUserFromTeamEntity(teamEntity team.TeamEntity, slackUserId, requesterEmail string) error { + for indexToDelete, userId := range teamEntity.SlackUserIds { + if userId == slackUserId { + teamEntity.SlackUserIds = append(teamEntity.SlackUserIds[:indexToDelete], teamEntity.SlackUserIds[indexToDelete+1:]...) + teamEntity.UpdatedBy = requesterEmail + err := teamService.teamRepository.UpdateTeam(&teamEntity) + if err != nil { + logger.Error(fmt.Sprintf("%s error in removing user from team with id: %d : %+v", logTag, teamEntity.ID, err)) + return customErrors.NewDataAccessError("Could not remove member from team") + } + return nil + } + } + return customErrors.NewNotFoundError("User not found in team") +} + +func findUserInTeam(emailId string, teamUsers []*teamUserModel.TeamUserDTO) *teamUserModel.TeamUserDTO { + for _, teamUserValue := range teamUsers { + if teamUserValue.User.Email == emailId { + return teamUserValue + } + } + return nil +} diff --git a/service/teamService/team_service_v2_interface.go b/service/teamService/team_service_v2_interface.go index 5a54a21..6decab7 100644 --- a/service/teamService/team_service_v2_interface.go +++ b/service/teamService/team_service_v2_interface.go @@ -2,6 +2,7 @@ package teamService import ( "houston/model/externalTeam" + "houston/model/team" teamRequest "houston/service/request/team" service "houston/service/response" ) @@ -11,4 +12,9 @@ type ITeamServiceV2 interface { GetAllTeams() ([]service.TeamResponse, error) GetExternalTeam(teamId uint, provider string) (*externalTeam.ExternalTeamDTO, error) AddTeam(request teamRequest.AddTeamRequest, userEmail string) (*service.AddTeamResponse, error) + UpdateTeam(request teamRequest.UpdateTeamRequest, userEmail string) error + RemoveTeamMember(teamId, userId uint, requesterEmail string) error + MakeManager(teamId, memberToMakeManagerId uint, requesterEmail string) error + GetTeam(teamId uint) (*service.TeamResponseV2, error) + GetTeamById(teamId uint) (*team.TeamEntity, error) } diff --git a/service/teamService/team_service_v2_test.go b/service/teamService/team_service_v2_test.go index 720f8db..be9e90c 100644 --- a/service/teamService/team_service_v2_test.go +++ b/service/teamService/team_service_v2_test.go @@ -15,6 +15,7 @@ import ( "houston/model/team" "houston/model/teamSeverity" "houston/model/teamUser" + "houston/model/teamUserSeverity" "houston/model/user" teamRequest "houston/service/request/team" service "houston/service/response" @@ -35,7 +36,7 @@ type TeamServiceV2Suite struct { teamService *TeamServiceV2 } -func (suite *TeamServiceV2Suite) SetupSuite() { +func (suite *TeamServiceV2Suite) SetupTest() { logger.InitLogger() suite.controller = minimock.NewController(suite.T()) suite.T().Cleanup(suite.controller.Finish) @@ -387,6 +388,427 @@ func (suite *TeamServiceV2Suite) Test_AddTeam_SuccessCase() { assert.NotNil(suite.T(), teamResponse) } +func (suite *TeamServiceV2Suite) Test_UpdateTeam_TeamFetchingFailureCase() { + suite.teamRepository.FindTeamByIdMock.Return(nil, errors.New("error")) + + err := suite.teamService.UpdateTeam(teamRequest.UpdateTeamRequest{Id: 1}, "") + + assert.EqualError(suite.T(), err, "error in fetching team with id: 1") +} + +func (suite *TeamServiceV2Suite) Test_UpdateTeam_UpdateSlackChannel_ConversationNotFound() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.slackService.GetConversationInfoMock.Return(nil, errors.New("error")) + + err := suite.teamService.UpdateTeam(teamRequest.UpdateTeamRequest{Id: 1, SlackChannel: "new-channel"}, "") + + assert.EqualError(suite.T(), err, "Unable to find Slack channel with the provided I.D. Please verify the I.D. and re-submit.") +} + +func (suite *TeamServiceV2Suite) Test_UpdateTeam_UpdateSlackChannel_RepoFailure() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.slackService.GetConversationInfoMock.Return(nil, nil) + suite.teamRepository.UpdateTeamMock.Return(errors.New("error")) + + err := suite.teamService.UpdateTeam(teamRequest.UpdateTeamRequest{Id: 1, SlackChannel: "new-channel"}, "") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_UpdateTeam_UpdateSlackChannel_Success() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.slackService.GetConversationInfoMock.Return(&slack.Channel{GroupConversation: slack.GroupConversation{Name: "new-channel"}}, nil) + suite.teamRepository.UpdateTeamMock.Return(nil) + + err := suite.teamService.UpdateTeam(teamRequest.UpdateTeamRequest{Id: 1, SlackChannel: "new-channel"}, "") + suite.Nil(err, "error should be nil") +} + +func (suite *TeamServiceV2Suite) Test_UpdateTeam_UpdateOncallUser_UserInfoError() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.userService.GetHoustonUserBySlackUserIdMock.Return(nil, errors.New("error")) + + err := suite.teamService.UpdateTeam(teamRequest.UpdateTeamRequest{Id: 1, OnCallId: "new-oncall"}, "") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_UpdateTeam_UpdateOncallUser_UserNotFound() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.userService.GetHoustonUserBySlackUserIdMock.Return(nil, nil) + + err := suite.teamService.UpdateTeam(teamRequest.UpdateTeamRequest{Id: 1, OnCallId: "new-oncall"}, "") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_UpdateTeam_UpdateOncallUser_RepoFailure() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.userService.GetHoustonUserBySlackUserIdMock.Return(GetMockUserDTO(), nil) + suite.teamRepository.UpdateTeamMock.Return(errors.New("error")) + + err := suite.teamService.UpdateTeam(teamRequest.UpdateTeamRequest{Id: 1, OnCallId: "new-oncall"}, "") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_UpdateTeam_UpdateOncallUser_Success() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.userService.GetHoustonUserBySlackUserIdMock.Return(GetMockUserDTO(), nil) + suite.teamRepository.UpdateTeamMock.Return(nil) + + err := suite.teamService.UpdateTeam(teamRequest.UpdateTeamRequest{Id: 1, OnCallId: "new-oncall"}, "") + suite.Nil(err, "error should be nil") +} + +func (suite *TeamServiceV2Suite) Test_UpdateTeam_UpdatePseOncallUser_UserInfoError() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.userService.GetHoustonUserBySlackUserIdMock.Return(nil, errors.New("error")) + + err := suite.teamService.UpdateTeam(teamRequest.UpdateTeamRequest{Id: 1, PseOnCallId: "new-pse-oncall"}, "") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_UpdateTeam_UpdatePseOncallUser_UserNotFound() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.userService.GetHoustonUserBySlackUserIdMock.Return(nil, nil) + + err := suite.teamService.UpdateTeam(teamRequest.UpdateTeamRequest{Id: 1, PseOnCallId: "new-pse-oncall"}, "") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_UpdateTeam_UpdatePseOncallUser_RepoFailure() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.userService.GetHoustonUserBySlackUserIdMock.Return(GetMockUserDTO(), nil) + suite.teamRepository.UpdateTeamMock.Return(errors.New("error")) + + err := suite.teamService.UpdateTeam(teamRequest.UpdateTeamRequest{Id: 1, PseOnCallId: "new-pse-oncall"}, "") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_UpdateTeam_UpdatePseOncallUser_Success() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.userService.GetHoustonUserBySlackUserIdMock.Return(GetMockUserDTO(), nil) + suite.teamRepository.UpdateTeamMock.Return(nil) + + err := suite.teamService.UpdateTeam(teamRequest.UpdateTeamRequest{Id: 1, PseOnCallId: "new-pse-oncall"}, "") + suite.Nil(err, "error should be nil") +} + +func (suite *TeamServiceV2Suite) Test_UpdateTeam_AddMembers_TeamSeverityMapError() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.teamSeverityService.GetSeveritiesMapForATeamMock.Return(nil, errors.New("error")) + + err := suite.teamService.UpdateTeam(teamRequest.UpdateTeamRequest{Id: 1, Members: &teamRequest.Members{ + SeverityId: 2, + UserEmailIds: []string{"test1@navi.com", "test2@navi.com"}, + }}, "") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_UpdateTeam_AddMembers_TeamUserError() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.teamSeverityService.GetSeveritiesMapForATeamMock.Return(GetMockTeamSeverityMap(), nil) + suite.teamUserService.GetTeamUsersByTeamIdMock.Return(nil, errors.New("error")) + + err := suite.teamService.UpdateTeam(teamRequest.UpdateTeamRequest{Id: 1, Members: &teamRequest.Members{ + SeverityId: 2, + UserEmailIds: []string{"test1@navi.com", "test2@navi.com"}, + }}, "") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_UpdateTeam_AddMembers_GetHoustonUserError() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.teamSeverityService.GetSeveritiesMapForATeamMock.Return(GetMockTeamSeverityMap(), nil) + suite.teamUserService.GetTeamUsersByTeamIdMock.Return(GetMockTeamUsers(), nil) + suite.userService.GetHoustonUserByEmailIdMock.Return(nil, errors.New("error")) + + err := suite.teamService.UpdateTeam(teamRequest.UpdateTeamRequest{Id: 1, Members: &teamRequest.Members{ + SeverityId: 2, + UserEmailIds: []string{"test1@navi.com", "test2@navi.com"}, + }}, "") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_UpdateTeam_AddMembers_GetHoustonUser_NilResponse() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.teamSeverityService.GetSeveritiesMapForATeamMock.Return(GetMockTeamSeverityMap(), nil) + suite.teamUserService.GetTeamUsersByTeamIdMock.Return(GetMockTeamUsers(), nil) + suite.userService.GetHoustonUserByEmailIdMock.Return(nil, nil) + + err := suite.teamService.UpdateTeam(teamRequest.UpdateTeamRequest{Id: 1, Members: &teamRequest.Members{ + SeverityId: 2, + UserEmailIds: []string{"test1@navi.com", "test2@navi.com"}, + }}, "") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_UpdateTeam_AddMembers_AddTeamUserError() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.teamSeverityService.GetSeveritiesMapForATeamMock.Return(GetMockTeamSeverityMap(), nil) + suite.teamUserService.GetTeamUsersByTeamIdMock.Return(GetMockTeamUsers(), nil) + suite.userService.GetHoustonUserByEmailIdMock.Return(GetMockUserDTO(), nil) + suite.teamUserService.AddTeamUserMock.Return(nil, errors.New("error")) + + err := suite.teamService.UpdateTeam(teamRequest.UpdateTeamRequest{Id: 1, Members: &teamRequest.Members{ + SeverityId: 2, + UserEmailIds: []string{"test1@navi.com", "test2@navi.com"}, + }}, "") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_UpdateTeam_AddMembers_AddTeamUserSeverityError() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.teamSeverityService.GetSeveritiesMapForATeamMock.Return(GetMockTeamSeverityMap(), nil) + suite.teamUserService.GetTeamUsersByTeamIdMock.Return(GetMockTeamUsers(), nil) + suite.userService.GetHoustonUserByEmailIdMock.Return(GetMockUserDTO(), nil) + suite.teamUserService.AddTeamUserMock.Return(GetMockTeamUserDTO(), nil) + suite.teamUserSeverityService.AddTeamUserSeverityMock.Return(errors.New("error")) + + err := suite.teamService.UpdateTeam(teamRequest.UpdateTeamRequest{Id: 1, Members: &teamRequest.Members{ + SeverityId: 2, + UserEmailIds: []string{"test1@navi.com", "test2@navi.com"}, + }}, "") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_UpdateTeam_AddMembers_UpdateTeamUserSeverityMappingError() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.teamSeverityService.GetSeveritiesMapForATeamMock.Return(GetMockTeamSeverityMap(), nil) + suite.teamUserService.GetTeamUsersByTeamIdMock.Return(GetMockTeamUsers(), nil) + suite.userService.GetHoustonUserByEmailIdMock.Return(GetMockUserDTO(), nil) + suite.teamUserService.AddTeamUserMock.Return(GetMockTeamUserDTO(), nil) + suite.teamUserSeverityService.AddTeamUserSeverityMock.Return(nil) + suite.teamUserSeverityService.UpdateTeamSeverityForTeamUserMock.Return(errors.New("error")) + + err := suite.teamService.UpdateTeam(teamRequest.UpdateTeamRequest{Id: 1, Members: &teamRequest.Members{ + SeverityId: 2, + UserEmailIds: []string{"test1@navi.com", "test2@navi.com"}, + }}, "") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_UpdateTeam_AddMembers_RepoCommitError() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.teamSeverityService.GetSeveritiesMapForATeamMock.Return(GetMockTeamSeverityMap(), nil) + suite.teamUserService.GetTeamUsersByTeamIdMock.Return(GetMockTeamUsers(), nil) + suite.userService.GetHoustonUserByEmailIdMock.Return(GetMockUserDTO(), nil) + suite.teamUserService.AddTeamUserMock.Return(GetMockTeamUserDTO(), nil) + suite.teamUserSeverityService.AddTeamUserSeverityMock.Return(nil) + suite.teamUserSeverityService.UpdateTeamSeverityForTeamUserMock.Return(nil) + suite.teamRepository.UpdateTeamMock.Return(errors.New("error")) + + err := suite.teamService.UpdateTeam(teamRequest.UpdateTeamRequest{Id: 1, Members: &teamRequest.Members{ + SeverityId: 2, + UserEmailIds: []string{"test1@navi.com", "test2@navi.com"}, + }}, "") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_UpdateTeam_AddMembers_Success() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.teamSeverityService.GetSeveritiesMapForATeamMock.Return(GetMockTeamSeverityMap(), nil) + suite.teamUserService.GetTeamUsersByTeamIdMock.Return(GetMockTeamUsers(), nil) + suite.userService.GetHoustonUserByEmailIdMock.Return(GetMockUserDTO(), nil) + suite.teamUserService.AddTeamUserMock.Return(GetMockTeamUserDTO(), nil) + suite.teamUserSeverityService.AddTeamUserSeverityMock.Return(nil) + suite.teamUserSeverityService.UpdateTeamSeverityForTeamUserMock.Return(nil) + suite.teamRepository.UpdateTeamMock.Return(nil) + + err := suite.teamService.UpdateTeam(teamRequest.UpdateTeamRequest{Id: 1, Members: &teamRequest.Members{ + SeverityId: 2, + UserEmailIds: []string{"test1@navi.com", "test2@navi.com"}, + }}, "") + suite.Nil(err, "error should be nil") +} + +func (suite *TeamServiceV2Suite) Test_RemoveTeamMember_TeamNotFound() { + suite.teamRepository.FindTeamByIdMock.Return(nil, errors.New("error")) + + err := suite.teamService.RemoveTeamMember(1, 1, "") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_RemoveTeamMember_UserError() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.userService.GetHoustonUserByIdMock.Return(nil, errors.New("error")) + + err := suite.teamService.RemoveTeamMember(1, 1, "") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_RemoveTeamMember_UserNotFound() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.userService.GetHoustonUserByIdMock.Return(nil, nil) + + err := suite.teamService.RemoveTeamMember(1, 1, "") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_RemoveTeamMember_ManagerRemovalCase() { + userDTO := GetMockUserDTO() + userDTO.SlackUserId = "123" + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.userService.GetHoustonUserByIdMock.Return(userDTO, nil) + + err := suite.teamService.RemoveTeamMember(1, 1, "") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_RemoveTeamMember_GetTeamUserError() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.userService.GetHoustonUserByIdMock.Return(GetMockUserDTO(), nil) + suite.teamUserService.GetTeamUserByTeamIdAndUserIdMock.Return(nil, errors.New("error")) + + err := suite.teamService.RemoveTeamMember(1, 1, "") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_RemoveTeamMember_RemoveTeamUserSeverityError() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.userService.GetHoustonUserByIdMock.Return(GetMockUserDTO(), nil) + suite.teamUserService.GetTeamUserByTeamIdAndUserIdMock.Return(GetMockTeamUserDTO(), nil) + suite.teamUserSeverityService.RemoveTeamUserSeverityByTeamUserIdMock.Return(errors.New("error")) + + err := suite.teamService.RemoveTeamMember(1, 1, "") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_RemoveTeamMember_RemoveTeamUserError() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.userService.GetHoustonUserByIdMock.Return(GetMockUserDTO(), nil) + suite.teamUserService.GetTeamUserByTeamIdAndUserIdMock.Return(GetMockTeamUserDTO(), nil) + suite.teamUserSeverityService.RemoveTeamUserSeverityByTeamUserIdMock.Return(nil) + suite.teamUserService.RemoveTeamUserMock.Return(errors.New("error")) + + err := suite.teamService.RemoveTeamMember(1, 1, "") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_RemoveTeamMember_TeamUpdateError() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.userService.GetHoustonUserByIdMock.Return(GetMockUserDTO(), nil) + suite.teamUserService.GetTeamUserByTeamIdAndUserIdMock.Return(GetMockTeamUserDTO(), nil) + suite.teamUserSeverityService.RemoveTeamUserSeverityByTeamUserIdMock.Return(nil) + suite.teamUserService.RemoveTeamUserMock.Return(nil) + suite.teamRepository.UpdateTeamMock.Return(errors.New("error")) + + err := suite.teamService.RemoveTeamMember(1, 1, "") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_RemoveTeamMember_Success() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.userService.GetHoustonUserByIdMock.Return(GetMockUserDTO(), nil) + suite.teamUserService.GetTeamUserByTeamIdAndUserIdMock.Return(GetMockTeamUserDTO(), nil) + suite.teamUserSeverityService.RemoveTeamUserSeverityByTeamUserIdMock.Return(nil) + suite.teamUserService.RemoveTeamUserMock.Return(nil) + suite.teamRepository.UpdateTeamMock.Return(nil) + + err := suite.teamService.RemoveTeamMember(1, 1, "") + suite.Nil(err, "error should be nil") +} + +func (suite *TeamServiceV2Suite) Test_MakeManager_TeamNotFound() { + suite.teamRepository.FindTeamByIdMock.Return(nil, errors.New("error")) + + err := suite.teamService.MakeManager(1, 1, "") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_MakeManager_TeamUserError() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.teamUserService.GetTeamUserByTeamIdAndUserIdMock.Return(nil, errors.New("error")) + + err := suite.teamService.MakeManager(1, 1, "") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_MakeManager_RepoUpdateError() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.teamUserService.GetTeamUserByTeamIdAndUserIdMock.Return(GetMockTeamUserDTO(), nil) + suite.teamRepository.UpdateTeamMock.Return(errors.New("error")) + + err := suite.teamService.MakeManager(1, 1, "") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_MakeManager_Success() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.teamUserService.GetTeamUserByTeamIdAndUserIdMock.Return(GetMockTeamUserDTO(), nil) + suite.teamRepository.UpdateTeamMock.Return(nil) + + err := suite.teamService.MakeManager(1, 1, "") + suite.Nil(err, "error should be nil") +} + +func (suite *TeamServiceV2Suite) Test_GetTeam_TeamInfoError() { + suite.teamRepository.FindTeamByIdMock.Return(nil, errors.New("error")) + + teamResponse, err := suite.teamService.GetTeam(1) + + assert.Nil(suite.T(), teamResponse) + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_GetTeam_TeamUserError() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.teamUserService.GetTeamUsersByTeamIdMock.Return(nil, errors.New("error")) + + teamResponse, err := suite.teamService.GetTeam(1) + + assert.Nil(suite.T(), teamResponse) + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_GetTeam_TeamSeverityMapError() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.teamUserService.GetTeamUsersByTeamIdMock.Return(GetMockTeamUsers(), nil) + suite.teamSeverityService.GetSeveritiesMapForATeamMock.Return(nil, errors.New("error")) + + teamResponse, err := suite.teamService.GetTeam(1) + + assert.Nil(suite.T(), teamResponse) + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_GetTeam_TeamUserSeverityError() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.teamUserService.GetTeamUsersByTeamIdMock.Return(GetMockTeamUsers(), nil) + suite.teamSeverityService.GetSeveritiesMapForATeamMock.Return(GetMockTeamSeverityMap(), nil) + suite.teamUserSeverityService.GetTeamUserSeveritiesByTeamSeverityIdMock.Return(nil, errors.New("error")) + + teamResponse, err := suite.teamService.GetTeam(1) + + assert.Nil(suite.T(), teamResponse) + suite.NotNil(err, "error should not be nil") +} + +func (suite *TeamServiceV2Suite) Test_GetTeam_BotAndChannelNotAvailable() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.teamUserService.GetTeamUsersByTeamIdMock.Return(GetMockTeamUsers(), nil) + suite.teamSeverityService.GetSeveritiesMapForATeamMock.Return(GetMockTeamSeverityMap(), nil) + suite.teamUserSeverityService.GetTeamUserSeveritiesByTeamSeverityIdMock.Return(GetMockTeamUserSeverities(), nil) + suite.userRepository.FindHoustonUserBySlackUserIdMock.Return(nil, errors.New("error")) + suite.slackService.GetConversationInfoMock.Return(nil, errors.New("error")) + + teamResponse, err := suite.teamService.GetTeam(1) + + assert.NotNil(suite.T(), teamResponse) + suite.Nil(err, "error should be nil") +} + +func (suite *TeamServiceV2Suite) Test_GetTeam_Success() { + suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamEntity(), nil) + suite.teamUserService.GetTeamUsersByTeamIdMock.Return(GetMockTeamUsers(), nil) + suite.teamSeverityService.GetSeveritiesMapForATeamMock.Return(GetMockTeamSeverityMap(), nil) + suite.teamUserSeverityService.GetTeamUserSeveritiesByTeamSeverityIdMock.Return(GetMockTeamUserSeverities(), nil) + suite.userRepository.FindHoustonUserBySlackUserIdMock.Return(GetMockUserEntity(), nil) + suite.slackService.GetConversationInfoMock.Return(GetMockSlackChannel(), nil) + + teamResponse, err := suite.teamService.GetTeam(1) + + assert.NotNil(suite.T(), teamResponse) + suite.Nil(err, "error should be nil") +} + func GetMockUserDTO() *user.UserDTO { return &user.UserDTO{ Email: "test@navi.com", @@ -397,8 +819,10 @@ func GetMockUserDTO() *user.UserDTO { func GetMockTeamEntity() *team.TeamEntity { return &team.TeamEntity{ - Name: "test team", - Active: true, + Name: "test team", + Active: true, + ManagerHandle: "123", + SlackUserIds: []string{"test"}, } } @@ -417,3 +841,41 @@ func GetMockTeamUserDTO() *teamUser.TeamUserDTO { UserID: 1, } } + +func GetMockTeamSeverityMap() map[uint]teamSeverity.TeamSeverityDTO { + return map[uint]teamSeverity.TeamSeverityDTO{ + 1: {ID: 1, TeamID: 1, SeverityID: 1}, + 2: {ID: 2, TeamID: 1, SeverityID: 2}, + 3: {ID: 3, TeamID: 1, SeverityID: 3}, + 4: {ID: 4, TeamID: 1, SeverityID: 4}, + } +} + +func GetMockTeamUsers() []*teamUser.TeamUserDTO { + return []*teamUser.TeamUserDTO{ + {ID: 1, TeamID: 1, UserID: 1, User: user.UserDTO{Email: "test3@navi.com"}}, + {ID: 2, TeamID: 1, UserID: 2, User: user.UserDTO{Email: "test2@navi.com"}}, + } +} + +func GetMockTeamUserSeverities() []teamUserSeverity.TeamUserSeverityDTO { + return []teamUserSeverity.TeamUserSeverityDTO{ + {ID: 1, TeamUser: 1, TeamSeverity: 1}, + {ID: 2, TeamUser: 1, TeamSeverity: 2}, + } +} + +func GetMockUserEntity() *user.UserEntity { + return &user.UserEntity{ + Model: gorm.Model{ID: 1}, + Email: "", + } +} + +func GetMockSlackChannel() *slack.Channel { + return &slack.Channel{ + GroupConversation: slack.GroupConversation{ + Name: "new-channel", + }, + } +} diff --git a/service/teamSeverity/impl/team_severity_service.go b/service/teamSeverity/impl/team_severity_service.go index d8fcb7d..a822ac9 100644 --- a/service/teamSeverity/impl/team_severity_service.go +++ b/service/teamSeverity/impl/team_severity_service.go @@ -62,3 +62,20 @@ func (service *TeamSeverityService) GetSeveritiesForATeam(teamID uint) ( return dtoConverter.TeamSeverityEntitiesToDTOs(teamSeverities), nil } + +// todo: figure out gorm map DTO +func (service *TeamSeverityService) GetSeveritiesMapForATeam(teamId uint) ( + map[uint]teamSeverityModel.TeamSeverityDTO, error, +) { + teamSeverities, err := service.GetSeveritiesForATeam(teamId) + if err != nil { + return nil, err + } + + teamSeveritiesMap := make(map[uint]teamSeverityModel.TeamSeverityDTO) + for _, teamSeverity := range teamSeverities { + teamSeveritiesMap[teamSeverity.SeverityID] = teamSeverity + } + + return teamSeveritiesMap, nil +} diff --git a/service/teamSeverity/impl/team_severity_service_test.go b/service/teamSeverity/impl/team_severity_service_test.go index 528befb..a705311 100644 --- a/service/teamSeverity/impl/team_severity_service_test.go +++ b/service/teamSeverity/impl/team_severity_service_test.go @@ -64,6 +64,24 @@ func (suite *TeamSeverityServiceSuite) Test_GetSeveritiesForATeam_Success() { suite.Equal(len(mockTeamSeverityEntities), len(severities), "length of severities should be equal") } +func (suite *TeamSeverityServiceSuite) Test_GetSeveritiesMapForATeam_RepoError() { + suite.teamSeverityRepository.GetTeamSeveritiesByTeamIdMock.Return(nil, errors.New("DB error")) + + severities, err := suite.teamSeverityService.GetSeveritiesMapForATeam(1) + suite.Nil(severities, "severities should be nil") + suite.EqualError(err, "DB error", "error should be DB error") +} + +func (suite *TeamSeverityServiceSuite) Test_GetSeveritiesMapForATeam_Success() { + mockTeamSeverityEntities := GetMockTeamSeverityEntities() + suite.teamSeverityRepository.GetTeamSeveritiesByTeamIdMock.Return(mockTeamSeverityEntities, nil) + + severities, err := suite.teamSeverityService.GetSeveritiesMapForATeam(1) + suite.NotNil(severities, "severities should not be nil") + suite.Nil(err, "error should be nil") + suite.Equal(len(mockTeamSeverityEntities), len(severities), "length of severities should be equal") +} + func GetMockSeverityDTOs() []severity.SeverityDTO { return []severity.SeverityDTO{ {ID: 1, Name: "Low", Description: "Low severity"}, diff --git a/service/teamSeverity/team_severity_service_interface.go b/service/teamSeverity/team_severity_service_interface.go index ea32379..2ca6a9f 100644 --- a/service/teamSeverity/team_severity_service_interface.go +++ b/service/teamSeverity/team_severity_service_interface.go @@ -5,4 +5,5 @@ import teamSeverityModel "houston/model/teamSeverity" type ITeamSeverityService interface { AddAllSeveritiesForATeam(teamID uint) ([]teamSeverityModel.TeamSeverityDTO, error) GetSeveritiesForATeam(teamID uint) ([]teamSeverityModel.TeamSeverityDTO, error) + GetSeveritiesMapForATeam(teamId uint) (map[uint]teamSeverityModel.TeamSeverityDTO, error) } diff --git a/service/teamUser/impl/team_user_service.go b/service/teamUser/impl/team_user_service.go index 0b4294d..d79989a 100644 --- a/service/teamUser/impl/team_user_service.go +++ b/service/teamUser/impl/team_user_service.go @@ -52,3 +52,33 @@ func (service *TeamUserService) GetTeamsByUserId(userId uint) ([]*teamUserModel. return dtoConverter.TeamUserEntitiesToDTOs(teamUsers), nil } + +func (service *TeamUserService) GetTeamUserByTeamIdAndUserId(teamId, userId uint) (*teamUserModel.TeamUserDTO, error) { + teamUser, err := service.teamUserRepository.GetTeamUserByTeamIdAndUserId(teamId, userId) + if err != nil { + logger.Error("error in fetching team user", zap.Error(err)) + return nil, err + } + + return teamUser.ToDTO(), nil +} + +func (service *TeamUserService) RemoveTeamUser(teamId, userId uint) error { + err := service.teamUserRepository.RemoveTeamUserByTeamIdAndUserId(teamId, userId) + if err != nil { + logger.Error("error in removing team user", zap.Error(err)) + return err + } + + return nil +} + +func (service *TeamUserService) GetTeamUserByTeamIdAndUserEmailId(teamId uint, userEmailId string) (*teamUserModel.TeamUserDTO, error) { + teamUser, err := service.teamUserRepository.GetTeamUserByTeamIdAndUserEmailId(teamId, userEmailId) + if err != nil { + logger.Error("error in fetching team user", zap.Error(err)) + return nil, err + } + + return teamUser.ToDTO(), nil +} diff --git a/service/teamUser/impl/team_user_service_test.go b/service/teamUser/impl/team_user_service_test.go index cf8f901..e610bb8 100644 --- a/service/teamUser/impl/team_user_service_test.go +++ b/service/teamUser/impl/team_user_service_test.go @@ -64,6 +64,54 @@ func (suite *TeamUserServiceSuite) Test_GetTeamsByUserId_SuccessCase() { suite.NotNil(teamUsers, "teamUsers should not be nil") } +func (suite *TeamUserServiceSuite) Test_GetTeamUserByTeamIdAndUserId_RepoFailureCase() { + suite.teamUserRepository.GetTeamUserByTeamIdAndUserIdMock.Return(teamUserModel.TeamUserEntity{}, errors.New("error")) + + teamUser, err := suite.teamUserService.GetTeamUserByTeamIdAndUserId(1, 1) + suite.Nil(teamUser, "teamUser should be nil") + suite.EqualError(err, "error", "error should be error") +} + +func (suite *TeamUserServiceSuite) Test_GetTeamUserByTeamIdAndUserId_SuccessCase() { + mockTeamUserEntity := GetMockTeamUserEntity(1, 1) + suite.teamUserRepository.GetTeamUserByTeamIdAndUserIdMock.Return(mockTeamUserEntity, nil) + + teamUser, err := suite.teamUserService.GetTeamUserByTeamIdAndUserId(1, 1) + suite.Nil(err, "error should be nil") + suite.NotNil(teamUser, "teamUser should not be nil") +} + +func (suite *TeamUserServiceSuite) Test_RemoveTeamUser_RepoFailureCase() { + suite.teamUserRepository.RemoveTeamUserByTeamIdAndUserIdMock.Return(errors.New("error")) + + err := suite.teamUserService.RemoveTeamUser(1, 1) + suite.EqualError(err, "error", "error should be error") +} + +func (suite *TeamUserServiceSuite) Test_RemoveTeamUser_SuccessCase() { + suite.teamUserRepository.RemoveTeamUserByTeamIdAndUserIdMock.Return(nil) + + err := suite.teamUserService.RemoveTeamUser(1, 1) + suite.Nil(err, "error should be nil") +} + +func (suite *TeamUserServiceSuite) Test_GetTeamUserByTeamIdAndUserEmailId_RepoFailureCase() { + suite.teamUserRepository.GetTeamUserByTeamIdAndUserEmailIdMock.Return(nil, errors.New("error")) + + teamUser, err := suite.teamUserService.GetTeamUserByTeamIdAndUserEmailId(1, "email") + suite.Nil(teamUser, "teamUser should be nil") + suite.EqualError(err, "error", "error should be error") +} + +func (suite *TeamUserServiceSuite) Test_GetTeamUserByTeamIdAndUserEmailId_SuccessCase() { + mockTeamUserEntity := GetMockTeamUserEntity(1, 1) + suite.teamUserRepository.GetTeamUserByTeamIdAndUserEmailIdMock.Return(&mockTeamUserEntity, nil) + + teamUser, err := suite.teamUserService.GetTeamUserByTeamIdAndUserEmailId(1, "email") + suite.Nil(err, "error should be nil") + suite.NotNil(teamUser, "teamUser should not be nil") +} + func (suite *TeamUserServiceSuite) SetupSuite() { logger.InitLogger() suite.teamUserRepository = mocks.NewTeamUserRepositoryMock(suite.T()) diff --git a/service/teamUser/team_user_service_interface.go b/service/teamUser/team_user_service_interface.go index 6810c6a..94b122d 100644 --- a/service/teamUser/team_user_service_interface.go +++ b/service/teamUser/team_user_service_interface.go @@ -5,5 +5,8 @@ import teamUserModel "houston/model/teamUser" type ITeamUserService interface { AddTeamUser(teamId, userId uint) (*teamUserModel.TeamUserDTO, error) GetTeamUsersByTeamId(teamId uint) ([]*teamUserModel.TeamUserDTO, error) + GetTeamUserByTeamIdAndUserId(teamId, userId uint) (*teamUserModel.TeamUserDTO, error) GetTeamsByUserId(userId uint) ([]*teamUserModel.TeamUserDTO, error) + RemoveTeamUser(teamId, userId uint) error + GetTeamUserByTeamIdAndUserEmailId(teamId uint, userEmailId string) (*teamUserModel.TeamUserDTO, error) } diff --git a/service/teamUserSeverity/impl/team_user_severity_service.go b/service/teamUserSeverity/impl/team_user_severity_service.go index c3f3975..4d017bf 100644 --- a/service/teamUserSeverity/impl/team_user_severity_service.go +++ b/service/teamUserSeverity/impl/team_user_severity_service.go @@ -3,6 +3,7 @@ package impl import ( teamUserSeverityModel "houston/model/teamUserSeverity" "houston/repository/teamUserSeverity" + "houston/service/dtoConverter" ) type TeamUserSeverityService struct { @@ -23,3 +24,16 @@ func (service *TeamUserSeverityService) AddTeamUserSeverity(teamUserId, teamSeve return service.teamUserSeverityRepository.AddTeamUserSeverity(teamUserSeverityEntity) } + +func (service *TeamUserSeverityService) GetTeamUserSeveritiesByTeamSeverityId(teamSeverityId uint) ([]teamUserSeverityModel.TeamUserSeverityDTO, error) { + teamUserSeverityEntities, err := service.teamUserSeverityRepository.GetTeamUserSeveritiesByTeamSeverityId(teamSeverityId) + return dtoConverter.TeamUserSeverityEntitiesToDTOs(teamUserSeverityEntities), err +} + +func (service *TeamUserSeverityService) UpdateTeamSeverityForTeamUser(teamUserId, teamSeverityId uint) error { + return service.teamUserSeverityRepository.UpdateTeamSeverityForTeamUser(teamUserId, teamSeverityId) +} + +func (service *TeamUserSeverityService) RemoveTeamUserSeverityByTeamUserId(teamUserId uint) error { + return service.teamUserSeverityRepository.RemoveTeamUserSeverityByTeamUserId(teamUserId) +} diff --git a/service/teamUserSeverity/impl/team_user_severity_service_test.go b/service/teamUserSeverity/impl/team_user_severity_service_test.go index fc2731b..e013643 100644 --- a/service/teamUserSeverity/impl/team_user_severity_service_test.go +++ b/service/teamUserSeverity/impl/team_user_severity_service_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/suite" "houston/logger" "houston/mocks" + "houston/model/teamUserSeverity" "testing" ) @@ -28,6 +29,61 @@ func (suite *TeamUserSeverityServiceSuite) Test_AddTeamUserSeverity_SuccessCase( suite.Nil(err, "error should be nil") } +func (suite *TeamUserSeverityServiceSuite) Test_GetTeamUserSeveritiesByTeamSeverityId_RepoFailureCase() { + suite.teamUserSeverityRepository.GetTeamUserSeveritiesByTeamSeverityIdMock.Return(nil, errors.New("error")) + + teamUserSeverities, err := suite.teamUserSeverityService.GetTeamUserSeveritiesByTeamSeverityId(1) + suite.Nil(teamUserSeverities, "teamUserSeverities should be nil") + suite.EqualError(err, "error", "error should be error") +} + +func (suite *TeamUserSeverityServiceSuite) Test_GetTeamUserSeveritiesByTeamSeverityId_SuccessCase() { + suite.teamUserSeverityRepository.GetTeamUserSeveritiesByTeamSeverityIdMock.Return(GetMockTeamUserSeverityEntities(), nil) + + teamUserSeverities, err := suite.teamUserSeverityService.GetTeamUserSeveritiesByTeamSeverityId(1) + suite.Nil(err, "error should be nil") + suite.NotNil(teamUserSeverities, "teamUserSeverities should not be nil") +} + +func (suite *TeamUserSeverityServiceSuite) Test_UpdateTeamSeverityForTeamUser_RepoFailureCase() { + suite.teamUserSeverityRepository.UpdateTeamSeverityForTeamUserMock.Return(errors.New("error")) + + err := suite.teamUserSeverityService.UpdateTeamSeverityForTeamUser(1, 1) + suite.EqualError(err, "error", "error should be error") +} + +func (suite *TeamUserSeverityServiceSuite) Test_UpdateTeamSeverityForTeamUser_SuccessCase() { + suite.teamUserSeverityRepository.UpdateTeamSeverityForTeamUserMock.Return(nil) + + err := suite.teamUserSeverityService.UpdateTeamSeverityForTeamUser(1, 1) + suite.Nil(err, "error should be nil") +} + +func (suite *TeamUserSeverityServiceSuite) Test_RemoveTeamUserSeverityByTeamUserId_RepoFailureCase() { + suite.teamUserSeverityRepository.RemoveTeamUserSeverityByTeamUserIdMock.Return(errors.New("error")) + + err := suite.teamUserSeverityService.RemoveTeamUserSeverityByTeamUserId(1) + suite.EqualError(err, "error", "error should be error") +} + +func (suite *TeamUserSeverityServiceSuite) Test_RemoveTeamUserSeverityByTeamUserId_SuccessCase() { + suite.teamUserSeverityRepository.RemoveTeamUserSeverityByTeamUserIdMock.Return(nil) + + err := suite.teamUserSeverityService.RemoveTeamUserSeverityByTeamUserId(1) + suite.Nil(err, "error should be nil") +} + +func GetMockTeamUserSeverityEntity() *teamUserSeverity.TeamUserSeverityEntity { + return &teamUserSeverity.TeamUserSeverityEntity{ + TeamUser: 1, + TeamSeverity: 1, + } +} + +func GetMockTeamUserSeverityEntities() []*teamUserSeverity.TeamUserSeverityEntity { + return []*teamUserSeverity.TeamUserSeverityEntity{GetMockTeamUserSeverityEntity(), GetMockTeamUserSeverityEntity()} +} + func (suite *TeamUserSeverityServiceSuite) SetupSuite() { logger.InitLogger() suite.teamUserSeverityRepository = mocks.NewTeamUserSeverityRepositoryMock(suite.T()) diff --git a/service/teamUserSeverity/team_user_severity_service_interface.go b/service/teamUserSeverity/team_user_severity_service_interface.go index 2825053..e50ca15 100644 --- a/service/teamUserSeverity/team_user_severity_service_interface.go +++ b/service/teamUserSeverity/team_user_severity_service_interface.go @@ -1,5 +1,10 @@ package teamUserSeverity +import teamUserSeverityModel "houston/model/teamUserSeverity" + type ITeamUserSeverityService interface { AddTeamUserSeverity(teamUserId, teamSeverityId uint) error + GetTeamUserSeveritiesByTeamSeverityId(teamSeverityId uint) ([]teamUserSeverityModel.TeamUserSeverityDTO, error) + UpdateTeamSeverityForTeamUser(teamUserId, teamSeverityId uint) error + RemoveTeamUserSeverityByTeamUserId(teamUserId uint) error } diff --git a/service/user/user_service.go b/service/user/user_service.go index 8a8b8f6..af8a91b 100644 --- a/service/user/user_service.go +++ b/service/user/user_service.go @@ -55,3 +55,18 @@ func (service *userServiceImpl) GetHoustonUserById(id uint) (*user.UserDTO, erro return userEntity.ToDTO(), nil } + +func (service *userServiceImpl) GetHoustonUserBySlackUserId(slackUserId string) (*user.UserDTO, error) { + userEntity, err := service.userRepository.FindHoustonUserBySlackUserId(slackUserId) + if err != nil { + logger.Error("error in fetching user by slack user id", zap.Error(err)) + return nil, err + } + + if userEntity == nil { + logger.Error(fmt.Sprintf("user with slack user id %s not found", slackUserId)) + return nil, nil + } + + return userEntity.ToDTO(), nil +} diff --git a/service/user/user_service_interface.go b/service/user/user_service_interface.go index 9300d31..f22e6c4 100644 --- a/service/user/user_service_interface.go +++ b/service/user/user_service_interface.go @@ -6,6 +6,7 @@ type UserService interface { GetHoustonUserByEmailId(emailId string) (*user.UserDTO, error) GetHoustonUserBySlackID(slackID string) (*user.UserDTO, error) GetHoustonUserById(id uint) (*user.UserDTO, error) + GetHoustonUserBySlackUserId(slackUserId string) (*user.UserDTO, error) } func NewUserService(userRepository user.IUserRepository) UserService { diff --git a/service/user/user_service_test.go b/service/user/user_service_test.go index d233d4b..5c30c43 100644 --- a/service/user/user_service_test.go +++ b/service/user/user_service_test.go @@ -65,6 +65,31 @@ func (suite *UserServiceSuite) Test_GetHoustonUserById_SuccessCase() { suite.NotNil(user, "user should not be nil") } +func (suite *UserServiceSuite) Test_GetHoustonUserBySlackID_RepoFailureCase() { + suite.userRepository.FindHoustonUserBySlackUserIdMock.Return(nil, errors.New("error")) + + user, err := suite.userService.GetHoustonUserBySlackID("slackUserId") + suite.NotNil(err, "error should not be nil") + suite.Nil(user, "user should be nil") +} + +func (suite *UserServiceSuite) Test_GetHoustonUserBySlackID_UserNotFoundCase() { + suite.userRepository.FindHoustonUserBySlackUserIdMock.Return(nil, nil) + + user, err := suite.userService.GetHoustonUserBySlackID("slackUserId") + suite.Nil(err, "error should be nil") + suite.Nil(user, "user should be nil") +} + +func (suite *UserServiceSuite) Test_GetHoustonUserBySlackID_SuccessCase() { + mockUserEntity := GetMockUserEntity() + suite.userRepository.FindHoustonUserBySlackUserIdMock.Return(&mockUserEntity, nil) + + user, err := suite.userService.GetHoustonUserBySlackID("slackUserId") + suite.Nil(err, "error should be nil") + suite.NotNil(user, "user should not be nil") +} + func (suite *UserServiceSuite) SetupSuite() { logger.InitLogger() suite.userRepository = mocks.NewIUserRepositoryMock(suite.T())