Files
houston-be/cmd/app/server.go
Gullipalli Chetan Kumar c393b81bbc TP-47335 : Update get teams api to reduce latency by getting user data from database instead of slack (#284)
* TP-47335| created teamservice version 2 for get teams api

* TP-47335| modified the getusers info function to handle nil error

* refactored the structure of team service and created interfaces

* TP-47335| created unit tests

* TP-47335| added unit tests for get teams api

* resolved PR comments

* created custom error types

* made some changes in unit tests

* added unit tests for team handler

* solved merge conflicts

* solved invalid users bug

* resolved merge conflicts

* restricting incident title length to 100 characters

* removed unecessary comments
2023-12-04 15:16:21 +05:30

305 lines
11 KiB
Go

package app
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/spf13/viper"
"go.uber.org/zap"
"gorm.io/gorm"
"houston/cmd/app/handler"
"houston/internal/clients"
"houston/internal/metrics"
"houston/logger"
"houston/model/ingester"
"houston/model/log"
"houston/model/team"
"houston/model/user"
"houston/pkg/rest"
"houston/pkg/slackbot"
rcaRepository "houston/repository/rca/impl"
"houston/repository/rcaInput"
"houston/service"
"houston/service/documentService"
"houston/service/incident"
"houston/service/rca"
"houston/service/slack"
"houston/service/teamService"
"net/http"
"strings"
"time"
)
type Server struct {
gin *gin.Engine
db *gorm.DB
mjolnirClient *clients.MjolnirClient
}
func NewServer(gin *gin.Engine, db *gorm.DB, mjolnirClient *clients.MjolnirClient) *Server {
return &Server{
gin: gin,
db: db,
mjolnirClient: mjolnirClient,
}
}
func (s *Server) Handler(houstonGroup *gin.RouterGroup) {
s.readinessHandler(houstonGroup)
s.gin.Use(s.metricMiddleware())
s.incidentClientHandler(houstonGroup)
s.filtersHandlerV2(houstonGroup)
s.rcaHandler(houstonGroup)
s.gin.Use(s.createMiddleware())
s.incidentClientHandlerV2(houstonGroup)
s.teamHandler(houstonGroup)
s.severityHandler(houstonGroup)
s.incidentHandler(houstonGroup)
s.logHandler(houstonGroup)
s.usersHandler(houstonGroup)
s.filtersHandler(houstonGroup)
metrics.AdminHandler()
//this should always be at the end since it opens websocket to connect to slackbot
s.houstonHandler()
}
func (s *Server) houstonHandler() {
houstonClient := NewHoustonClient()
houstonHandler := handler.NewSlackHandler(s.db, houstonClient.socketModeClient)
houstonHandler.HoustonConnect()
}
func (s *Server) teamHandler(houstonGroup *gin.RouterGroup) {
houstonClient := NewHoustonClient()
slackClient := slackbot.NewSlackClient(houstonClient.socketModeClient)
authService := service.NewAuthService(s.mjolnirClient, slackClient)
teamHandler := service.NewTeamService(s.gin, s.db, slackClient, authService)
//Will be deprecated because they are not using houston group
s.gin.GET("/teams", teamHandler.GetTeams)
s.gin.GET("/teams/:id", teamHandler.GetTeams)
s.gin.POST("/teams", teamHandler.UpdateTeam)
if viper.GetBool("get.teams.v2.enabled") {
logRepository := log.NewLogRepository(s.db)
teamRepository := team.NewTeamRepository(s.db, logRepository)
userRepository := user.NewUserRepository(s.db)
slackService := slack.NewSlackService()
teamServiceV2 := teamService.NewTeamServiceV2(teamRepository, userRepository, slackService)
teamHandlerV2 := handler.NewTeamHandler(s.gin, teamServiceV2)
houstonGroup.GET("/teams", teamHandlerV2.HandleGetAllTeams)
houstonGroup.GET("/teams/:id", teamHandlerV2.HandleGetTeamDetails)
} else {
houstonGroup.GET("/teams", teamHandler.GetTeams)
houstonGroup.GET("/teams/:id", teamHandler.GetTeams)
}
houstonGroup.POST("/teams/add", teamHandler.AddTeam)
houstonGroup.POST("/teams", teamHandler.UpdateTeam)
houstonGroup.PATCH("/teams/:id/manager/:userId", teamHandler.MakeManager)
houstonGroup.DELETE("/teams/:id/members/:userId", teamHandler.RemoveTeamMember)
houstonGroup.DELETE("/teams/:id", teamHandler.RemoveTeam)
}
func (s *Server) severityHandler(houstonGroup *gin.RouterGroup) {
houstonClient := NewHoustonClient()
slackClient := slackbot.NewSlackClient(houstonClient.socketModeClient)
severityHandler := service.NewSeverityService(s.gin, s.db, slackClient)
//Will be deprecated because they are not using hosuton group
s.gin.GET("/severities", severityHandler.GetSeverities)
s.gin.GET("/severities/:id", severityHandler.GetSeverities)
s.gin.POST("/severities", severityHandler.UpdateSeverities)
houstonGroup.GET("/severities", severityHandler.GetSeverities)
houstonGroup.GET("/severities/:id", severityHandler.GetSeverities)
houstonGroup.POST("/severities", severityHandler.UpdateSeverities)
}
func (s *Server) incidentClientHandler(houstonGroup *gin.RouterGroup) {
houstonClient := NewHoustonClient()
incidentHandler := service.NewIncidentService(s.gin, s.db, houstonClient.socketModeClient)
// Add a header to the routes in houstonGroup
houstonGroup.Use(func(c *gin.Context) {
// Add your desired header key-value pair
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, PATCH, GET, PUT, DELETE")
c.Writer.Header().Set("Access-Control-Allow-Headers", viper.GetString("allowed.custom.headers"))
origin := c.Request.Header.Get("Origin")
c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
})
//Will be deprecated because they are not using hosuton group
s.gin.POST("/create-incident", incidentHandler.CreateIncident)
//TODO- Remove these api. Provide Multi Realm support in Internal Web BFF
houstonGroup.GET("/incidents/unsecured/v2/:id", incidentHandler.GetIncidents)
houstonGroup.POST("/create-incident", incidentHandler.CreateIncident)
}
func (s *Server) incidentClientHandlerV2(houstonGroup *gin.RouterGroup) {
houstonGroup.Use(func(c *gin.Context) {
// Add your desired header key-value pair
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT")
c.Writer.Header().Set("Access-Control-Allow-Headers", viper.GetString("allowed.custom.headers"))
origin := c.Request.Header.Get("Origin")
c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
})
houstonClient := NewHoustonClient()
slackClient := slackbot.NewSlackClient(houstonClient.socketModeClient)
authService := service.NewAuthService(s.mjolnirClient, slackClient)
incidentServiceV2 := incident.NewIncidentServiceV2(s.db)
incidentHandler := handler.NewIncidentHandler(s.gin, s.db, authService, incidentServiceV2)
houstonGroup.POST("/create-incident-v2", incidentHandler.HandleCreateIncident)
if viper.GetBool("UPDATE_INCIDENT_V2_ENABLED") {
s.gin.POST("/incidents", incidentHandler.HandleUpdateIncident)
houstonGroup.POST("/incidents", incidentHandler.HandleUpdateIncident)
} else {
incidentHandlerV1 := service.NewIncidentService(s.gin, s.db, houstonClient.socketModeClient)
s.gin.POST("/incidents", incidentHandlerV1.UpdateIncident)
houstonGroup.POST("/incidents", incidentHandlerV1.UpdateIncident)
}
houstonGroup.POST("/link-jira-to-incident", incidentHandler.HandleJiraLinking)
houstonGroup.POST("/unlink-jira-from-incident", incidentHandler.HandleJiraUnLinking)
}
func (s *Server) incidentHandler(houstonGroup *gin.RouterGroup) {
houstonClient := NewHoustonClient()
incidentHandler := service.NewIncidentService(s.gin, s.db, houstonClient.socketModeClient)
//Will be deprecated because they are not using hosuton group
s.gin.GET("/incidents", incidentHandler.GetIncidents)
s.gin.GET("/incidents/:id", incidentHandler.GetIncidents)
s.gin.GET("/incidents/header", incidentHandler.GetIncidentHeader)
houstonGroup.GET("/incidents", incidentHandler.GetIncidents)
houstonGroup.GET("/incidents/:id", incidentHandler.GetIncidents)
houstonGroup.GET("/incidents/header", incidentHandler.GetIncidentHeader)
houstonGroup.GET("/teamIncidents/:teamId", incidentHandler.GetTeamIncidents)
}
func (s *Server) logHandler(houstonGroup *gin.RouterGroup) {
logHandler := service.NewLogService(s.gin, s.db)
houstonGroup.GET("/logs/:log_type/:id", logHandler.GetLogs)
}
func (s *Server) rcaHandler(houstonGroup *gin.RouterGroup) {
slackService := slack.NewSlackService()
incidentServiceV2 := incident.NewIncidentServiceV2(s.db)
rcaRepository := rcaRepository.NewRcaRepository(s.db)
rcaInputRepository := rcaInput.NewRcaInputRepository(s.db)
restClient := rest.NewHttpRestClient()
documentService := documentService.NewActionsImpl(restClient)
userRepository := user.NewUserRepository(s.db)
rcaService := rca.NewRcaService(incidentServiceV2, slackService, restClient, documentService, rcaRepository, rcaInputRepository, userRepository)
rcaHandler := handler.NewRcaHandler(s.gin, rcaService)
houstonGroup.POST("/rca", rcaHandler.HandlePostRca)
}
func (s *Server) usersHandler(houstonGroup *gin.RouterGroup) {
houstonClient := NewHoustonClient()
slackClient := slackbot.NewSlackClient(houstonClient.socketModeClient)
authService := service.NewAuthService(s.mjolnirClient, slackClient)
usersHandler := service.NewUserService(s.gin, slackClient, s.db, houstonClient.socketModeClient, authService)
//Will be deprecated because they are not using hosuton group
s.gin.GET("/users/:id", usersHandler.GetUserInfo)
s.gin.GET("/users", usersHandler.GetUsersInConversation)
houstonGroup.GET("/users/:id", usersHandler.GetUserInfo)
houstonGroup.GET("/users", usersHandler.GetUsersInConversation)
houstonGroup.GET("/users/update", usersHandler.UpdateHoustonUsers)
houstonGroup.GET("/bots", usersHandler.GetAllHoustonUserBots)
}
func (s *Server) filtersHandler(houstonGroup *gin.RouterGroup) {
filtersHandler := service.NewFilterService(s.gin, s.db)
//Will be deprecated because they are not using hosuton group
s.gin.GET("/filters", filtersHandler.GetFilters)
houstonGroup.GET("/filters", filtersHandler.GetFilters)
}
// to be removed via internal bff
func (s *Server) filtersHandlerV2(houstonGroup *gin.RouterGroup) {
filtersHandler := service.NewFilterService(s.gin, s.db)
houstonGroup.GET("/filters/v2", filtersHandler.GetFilters)
}
func (s *Server) readinessHandler(houstonGroup *gin.RouterGroup) {
readinessHandler := service.NewReadinessService(s.gin)
//Will be deprecated because they are not using hosuton group
s.gin.GET("/ping", readinessHandler.Ping)
houstonGroup.GET("/ping", readinessHandler.Ping)
}
func (s *Server) Start() {
logger.Info("starting houston server", zap.String("port", viper.GetString("port")))
s.gin.Run(fmt.Sprintf(":%v", "8080"))
}
func (s *Server) metricMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
startTime := time.Now()
c.Next()
endTime := time.Now()
duration := endTime.Sub(startTime)
metricsPublisher := metrics.NewMetricPublisher()
apiMetrics := ingester.ApiMetric{
Url: c.Request.URL.Path,
Method: c.Request.Method,
ResponseCode: c.Writer.Status(),
StartTime: startTime.Unix(),
EndTime: endTime.Unix(),
DurationInMs: duration.Milliseconds(),
BytesSent: c.Writer.Size(),
}
metricsPublisher.PublishMetrics(ingester.MetricAttributes{ApiMetric: apiMetrics}, ingester.API_METRICS)
}
}
func (s *Server) createMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
whitelistedDomains := getWhitelistedDomains()
//auth handling
isAuthEnabled := viper.GetBool("auth.enabled")
if isAuthEnabled {
sessionResponse, err := s.mjolnirClient.GetSessionResponse(c.Request.Header.Get("X-Session-Token"))
if err != nil || sessionResponse.StatusCode == 401 {
c.AbortWithStatus(http.StatusUnauthorized)
return
}
}
//cors handling
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, PATCH, DELETE, OPTIONS, GET, PUT")
c.Writer.Header().Set("Access-Control-Allow-Headers", viper.GetString("allowed.custom.headers"))
origin := c.Request.Header.Get("Origin")
if !whitelistedDomains[origin] {
c.AbortWithStatus(http.StatusUnauthorized)
return
}
c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(http.StatusOK)
return
}
}
}
func getWhitelistedDomains() map[string]bool {
allowedList := make(map[string]bool)
domains := strings.Split(viper.GetString("whitelisted.domains"), ",")
for _, domain := range domains {
domainLocal := domain
allowedList[domainLocal] = true
}
return allowedList
}