* INFRA-3098 : Auto escalate refactor * INFRA-3098 : Delete old cron code * INFRA-2887 : File and api name changes * INFRA-2887 : Code review comments
378 lines
15 KiB
Go
378 lines
15 KiB
Go
package app
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/spf13/viper"
|
|
"go.uber.org/zap"
|
|
"gorm.io/gorm"
|
|
"houston/appcontext"
|
|
"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"
|
|
"houston/repository/externalTeamRepo"
|
|
rcaRepository "houston/repository/rca/impl"
|
|
"houston/repository/rcaInput"
|
|
"houston/service"
|
|
"houston/service/documentService"
|
|
incidentService "houston/service/incident/impl"
|
|
"houston/service/orchestration"
|
|
rcaService "houston/service/rca/impl"
|
|
"houston/service/slack"
|
|
"houston/service/teamService"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type Server struct {
|
|
gin *gin.Engine
|
|
db *gorm.DB
|
|
mjolnirClient *clients.MjolnirClient
|
|
authService *service.AuthService
|
|
}
|
|
|
|
func NewServer(gin *gin.Engine, db *gorm.DB, mjolnirClient *clients.MjolnirClient) *Server {
|
|
return &Server{
|
|
gin: gin,
|
|
db: db,
|
|
mjolnirClient: mjolnirClient,
|
|
authService: service.NewAuthService(mjolnirClient, slackbot.NewSlackClient(nil)),
|
|
}
|
|
}
|
|
|
|
func (s *Server) Handler(houstonGroup *gin.RouterGroup) {
|
|
s.readinessHandler(houstonGroup)
|
|
houstonGroup.Use(s.metricMiddleware())
|
|
s.gin.Use(s.metricMiddleware())
|
|
metrics.AdminHandler()
|
|
s.incidentClientHandler(houstonGroup)
|
|
s.filtersHandlerV2(houstonGroup)
|
|
s.rcaHandler(houstonGroup)
|
|
s.gin.Use(s.createMiddleware())
|
|
s.incidentClientHandlerV2(houstonGroup)
|
|
s.productsHandler(houstonGroup)
|
|
s.productTeamsHandler(houstonGroup)
|
|
s.reminderHandler(houstonGroup)
|
|
s.teamHandler(houstonGroup)
|
|
s.severityHandler(houstonGroup)
|
|
s.incidentHandler(houstonGroup)
|
|
s.logHandler(houstonGroup)
|
|
s.usersHandler(houstonGroup)
|
|
s.tagHandler(houstonGroup)
|
|
s.filtersHandler(houstonGroup)
|
|
|
|
//this should always be at the end since it opens websocket to connect to slackbot
|
|
s.houstonHandler()
|
|
}
|
|
|
|
func (s *Server) houstonHandler() {
|
|
incidentServiceV2 := incidentService.NewIncidentServiceV2(s.db)
|
|
incidentOrchestrator := orchestration.NewIncidentOrchestrator(
|
|
appcontext.GetProductTeamsService(),
|
|
appcontext.GetProductsService(),
|
|
appcontext.GetTeamUserService(),
|
|
appcontext.GetUserService(),
|
|
appcontext.GetTeamService(),
|
|
appcontext.GetSeverityService(),
|
|
appcontext.GetSlackService(),
|
|
incidentServiceV2,
|
|
)
|
|
houstonClient := NewHoustonClient()
|
|
houstonHandler := handler.NewSlackHandler(s.db, houstonClient.socketModeClient, incidentOrchestrator)
|
|
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)
|
|
|
|
logRepository := log.NewLogRepository(s.db)
|
|
teamRepository := team.NewTeamRepository(s.db, logRepository)
|
|
userRepository := user.NewUserRepository(s.db)
|
|
slackService := slack.NewSlackService()
|
|
externalTeamRepository := externalTeamRepo.NewExternalTeamRepository(s.db)
|
|
teamServiceV2 := teamService.NewTeamServiceV2(
|
|
teamRepository, userRepository, slackService, externalTeamRepository,
|
|
appcontext.GetTeamSeverityService(), appcontext.GetUserService(), appcontext.GetTeamUserService(), appcontext.GetTeamUserSeverityService())
|
|
teamHandlerV2 := handler.NewTeamHandler(s.gin, teamServiceV2)
|
|
|
|
if viper.GetBool("get.teams.v2.enabled") {
|
|
houstonGroup.GET("/teams", teamHandlerV2.HandleGetAllTeams)
|
|
houstonGroup.GET("/teams/:id", teamHandlerV2.HandleGetTeamDetails)
|
|
} else {
|
|
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/members", s.authService.IfMemberOrAdmin(teamHandlerV2.HandleAddMembers))
|
|
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)
|
|
}
|
|
|
|
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)
|
|
houstonGroup.DELETE("/severity/:id/member/:emailID", s.authService.IfAdmin(severityHandler.RemoveMember))
|
|
}
|
|
|
|
func (s *Server) incidentClientHandler(houstonGroup *gin.RouterGroup) {
|
|
|
|
houstonClient := NewHoustonClient()
|
|
incidentHandler := service.NewIncidentService(s.gin, s.db, houstonClient.socketModeClient, appcontext.GetTeamService())
|
|
// 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) productsHandler(houstonGroup *gin.RouterGroup) {
|
|
productsHandler := handler.NewProductHandler(s.gin)
|
|
|
|
houstonGroup.POST("/product", s.authService.IfAdmin(productsHandler.HandleCreateProduct))
|
|
houstonGroup.PUT("/product/:id", s.authService.IfAdmin(productsHandler.HandleUpdateProduct))
|
|
houstonGroup.DELETE("/product/:id", s.authService.IfAdmin(productsHandler.HandleDeleteProductByID))
|
|
houstonGroup.GET("/products", productsHandler.HandleGetAllProducts)
|
|
houstonGroup.GET("/product/:id", productsHandler.HandleGetProductByID)
|
|
}
|
|
|
|
func (s *Server) productTeamsHandler(houstonGroup *gin.RouterGroup) {
|
|
productTeamsHandler := handler.NewProductTeamHandler(s.gin)
|
|
|
|
houstonGroup.POST("/productTeamsMapping", s.authService.IfAdmin(productTeamsHandler.HandleCreateProductTeamMapping))
|
|
houstonGroup.GET("/productTeamsMapping", productTeamsHandler.HandleGetAllProductTeamsMapping)
|
|
}
|
|
|
|
func (s *Server) reminderHandler(houstonGroup *gin.RouterGroup) {
|
|
reminderHandler := handler.NewReminderHandler(s.gin)
|
|
houstonGroup.POST("reminder/team-incidents", reminderHandler.HandleTeamIncidents)
|
|
}
|
|
|
|
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 := incidentService.NewIncidentServiceV2(s.db)
|
|
incidentOrchestrator := orchestration.NewIncidentOrchestrator(
|
|
appcontext.GetProductTeamsService(),
|
|
appcontext.GetProductsService(),
|
|
appcontext.GetTeamUserService(),
|
|
appcontext.GetUserService(),
|
|
appcontext.GetTeamService(),
|
|
appcontext.GetSeverityService(),
|
|
appcontext.GetSlackService(),
|
|
incidentServiceV2,
|
|
)
|
|
incidentHandler := handler.NewIncidentHandler(s.gin, s.db, incidentServiceV2, incidentOrchestrator)
|
|
houstonGroup.POST("/create-incident-v2", incidentHandler.HandleCreateIncident)
|
|
houstonGroup.POST("/create-incident-v3", incidentHandler.HandleCreateIncidentV3)
|
|
|
|
s.gin.POST("/incidents", authService.IfValidHoustonUser(incidentHandler.HandleUpdateIncident))
|
|
houstonGroup.POST("/incidents", incidentHandler.HandleUpdateIncident)
|
|
houstonGroup.POST("/incidents/resolve", authService.IfValidHoustonUser(incidentHandler.HandleResolveIncident))
|
|
houstonGroup.POST("/incidents/escalate", incidentHandler.HandleEscalateIncident)
|
|
houstonGroup.POST("/link-jira-to-incident", incidentHandler.HandleJiraLinking)
|
|
houstonGroup.POST("/unlink-jira-from-incident", incidentHandler.HandleJiraUnLinking)
|
|
houstonGroup.GET("/get-jira-statuses", incidentHandler.HandleGetJiraStatuses)
|
|
houstonGroup.GET("/user/products", incidentHandler.HandleGetProductsOfUser)
|
|
houstonGroup.GET("/product/reporting-and-responder-teams", incidentHandler.HandleGetReportingAndResponderTeams)
|
|
}
|
|
|
|
func (s *Server) incidentHandler(houstonGroup *gin.RouterGroup) {
|
|
houstonClient := NewHoustonClient()
|
|
incidentHandler := service.NewIncidentService(s.gin, s.db, houstonClient.socketModeClient, appcontext.GetTeamService())
|
|
|
|
//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 := incidentService.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 := rcaService.NewRcaService(incidentServiceV2, slackService, documentService, rcaRepository,
|
|
rcaInputRepository, userRepository, appcontext.GetDriveService())
|
|
rcaHandler := handler.NewRcaHandler(s.gin, rcaService)
|
|
|
|
houstonGroup.POST("/rca", rcaHandler.HandlePostRca)
|
|
houstonGroup.GET("/conversation/:incidentId", rcaHandler.HandleGetConversationUrls)
|
|
}
|
|
|
|
func (s *Server) tagHandler(houstonGroup *gin.RouterGroup) {
|
|
tagService := appcontext.GetTagService()
|
|
tagHandler := handler.NewTagHandler(s.gin, tagService)
|
|
|
|
houstonGroup.GET("/tags/resolve", tagHandler.HandleGetTagsForIncidentResolution)
|
|
}
|
|
|
|
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.FullPath(),
|
|
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.HttpClientRequestMetrics)
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|