From dcb291f7e9e1a6d8e7d2ee636ea9638e957ba29c Mon Sep 17 00:00:00 2001 From: "aman.singh" Date: Thu, 12 Sep 2024 02:59:54 +0530 Subject: [PATCH] first commit --- .gitignore | 2 + configs/application.yml | 6 +- configs/config.go | 6 + configs/houston_config.go | 17 ++ cybertron-log-enricher | 1 + internal/dependencies/dependencies.go | 11 +- internal/transport/handler/houston.go | 29 +++ internal/transport/router/exception.go | 1 + internal/transport/router/houston.go | 17 ++ internal/transport/server.go | 4 +- models/db/houston.go | 6 + models/db/project.go | 1 + pkg/houstonClient/houstonClient.go | 293 +++++++++++++++++++++++++ service/HoustonService.go | 146 ++++++++++++ service/ProjectCreator.go | 14 +- 15 files changed, 544 insertions(+), 10 deletions(-) create mode 100644 configs/houston_config.go create mode 160000 cybertron-log-enricher create mode 100644 internal/transport/handler/houston.go create mode 100644 internal/transport/router/houston.go create mode 100644 models/db/houston.go create mode 100644 pkg/houstonClient/houstonClient.go create mode 100644 service/HoustonService.go diff --git a/.gitignore b/.gitignore index c49a829..dc2db0a 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,5 @@ vendor/ go.sum tmp/ + +air diff --git a/configs/application.yml b/configs/application.yml index 7ca3bf6..dffa4d4 100644 --- a/configs/application.yml +++ b/configs/application.yml @@ -18,7 +18,7 @@ db: open: 300 username: postgres password: admin - host: localhost + host: office-server.tail3fba9.ts.net port: 5432 name: cybertron_dev ssl: @@ -82,3 +82,7 @@ aws: mjolnir: service.url: https://qa-mjolnir-service.np.navi-ppl.in realm.id: ZicSxsvBwE + +houston: + service.url: https://qa-houston.np.navi-sa.in + realm.id: ZicSxsvBwE diff --git a/configs/config.go b/configs/config.go index 41f6750..b4b88af 100644 --- a/configs/config.go +++ b/configs/config.go @@ -22,6 +22,7 @@ type AppConfig struct { KafkaConfig *KafkaConfig ElasticConfig *ElasticConfig mjolnir *MjolnirClientConfig + houston *HoustonClientConfig } type MigConfig struct { @@ -49,6 +50,7 @@ func LoadConfig() { KafkaConfig: NewKafkaConfig(), ElasticConfig: NewElasticConfig(), mjolnir: NewMjolnirConfig(), + houston: NewHoustonConfig(), } } @@ -118,3 +120,7 @@ func GetElasticConfig() *ElasticConfig { func GetMjolnirConfig() *MjolnirClientConfig { return appConfig.mjolnir } + +func GetHoustonConfig() *HoustonClientConfig { + return appConfig.houston +} diff --git a/configs/houston_config.go b/configs/houston_config.go new file mode 100644 index 0000000..2f41daf --- /dev/null +++ b/configs/houston_config.go @@ -0,0 +1,17 @@ +package configs + +type HoustonClientConfig struct { + baseUrl string + realmId string +} + +func NewHoustonConfig() *HoustonClientConfig { + + return &HoustonClientConfig{ + baseUrl: getString("houston.service.url", true), + } +} + +func (p *HoustonClientConfig) GetHoustonBaseUrl() string { + return p.baseUrl +} diff --git a/cybertron-log-enricher b/cybertron-log-enricher new file mode 160000 index 0000000..d867d9f --- /dev/null +++ b/cybertron-log-enricher @@ -0,0 +1 @@ +Subproject commit d867d9f3654ed9b9b865922bf454c211f0a6d7e7 diff --git a/internal/dependencies/dependencies.go b/internal/dependencies/dependencies.go index 624bcce..ace44d7 100644 --- a/internal/dependencies/dependencies.go +++ b/internal/dependencies/dependencies.go @@ -8,6 +8,7 @@ import ( "cybertron/internal/database" "cybertron/internal/transport/handler" "cybertron/pkg/db" + "cybertron/pkg/houstonClient" httpclient "cybertron/pkg/httpClient" "cybertron/pkg/kafka/producer" "cybertron/pkg/log" @@ -35,6 +36,7 @@ type Service struct { AuthService *service.AuthService SearchService *service.SearchService S3Client *aws.Actions + // Add your service here } @@ -44,6 +46,7 @@ type Handler struct { ReleaseHandler *handler.ReleasesHandler ExceptionHandler *handler.ExceptionHandler SearchHandler *handler.SearchHandler + HoustonHandler *handler.HoustonHandler } type Repositories struct { @@ -61,6 +64,7 @@ func InitDependencies() *Dependencies { kafkaProducer := initKafkaProducer() elasticSearch, _ := elastic.NewElasticClient(*configs.GetElasticConfig()) mjolnirClient := mjolnirClient.NewMjolnirClient(*configs.GetMjolnirConfig()) + houstonClient := houstonClient.NewHoustonClient(*configs.GetHoustonConfig()) documentServiceClient := document.NewDocumentServiceHttpClient(httpClient, logger, configs.GetDocumentServiceHttpClientConfigs()) projectServiceClient := service.NewProjectCreator(logger, dbClient, s3Client, kafkaProducer) @@ -69,9 +73,10 @@ func InitDependencies() *Dependencies { exceptionServiceClient := service.NewExceptionService(logger, dbClient, kafkaProducer) searchServiceClient := service.NewSearchService(logger, elasticSearch) authService := service.NewAuthService(mjolnirClient) + houstonService := service.NewHoustonService(logger, dbClient, kafkaProducer, houstonClient) services := initServices(documentServiceClient, projectServiceClient, sourceMapServiceClient, releaseServiceClient, exceptionServiceClient, searchServiceClient, authService) - handlers := initHandlers(projectServiceClient, sourceMapServiceClient, releaseServiceClient, exceptionServiceClient, searchServiceClient) + handlers := initHandlers(projectServiceClient, sourceMapServiceClient, releaseServiceClient, exceptionServiceClient, searchServiceClient, houstonService) return &Dependencies{ Service: services, @@ -102,18 +107,20 @@ func initRepositories(dbClient *gorm.DB) *Repositories { } } -func initHandlers(projectService *service.ProjectCreator, sourceMapService *service.SourceMapService, releaseService *service.ReleaseService, exceotionService *service.ExceptionService, searchService *service.SearchService) *Handler { +func initHandlers(projectService *service.ProjectCreator, sourceMapService *service.SourceMapService, releaseService *service.ReleaseService, exceotionService *service.ExceptionService, searchService *service.SearchService, houstonService *service.HoustonService) *Handler { projectHandler := handler.NewProjectHandler(projectService) sourceMapHandler := handler.NewSourceMapHandler(sourceMapService) releaseHandler := handler.NewReleaseHandler(releaseService) exceptionHandler := handler.NewExceptionHandler(exceotionService) searchHandler := handler.NewSearchHandler(searchService) + houstonHandler := handler.NewHoustonHandler(houstonService) return &Handler{ ProjectHandler: projectHandler, SourceMapHandler: sourceMapHandler, ReleaseHandler: releaseHandler, ExceptionHandler: exceptionHandler, SearchHandler: searchHandler, + HoustonHandler: houstonHandler, } } diff --git a/internal/transport/handler/houston.go b/internal/transport/handler/houston.go new file mode 100644 index 0000000..72f3382 --- /dev/null +++ b/internal/transport/handler/houston.go @@ -0,0 +1,29 @@ +package handler + +import ( + "cybertron/service" + + "github.com/gin-gonic/gin" +) + +type HoustonHandler struct { + houstonService *service.HoustonService +} + +func (h *HoustonHandler) CreateHouston(c *gin.Context) { + h.houstonService.CreateHouston(c) +} + +func (h *HoustonHandler) GetProducts(c *gin.Context) { + h.houstonService.GetProducts(c) +} + +func (h *HoustonHandler) GetResponderTeam(c *gin.Context) { + h.houstonService.GetResponderTeam(c) +} + +func NewHoustonHandler(s *service.HoustonService) *HoustonHandler { + return &HoustonHandler{ + houstonService: s, + } +} diff --git a/internal/transport/router/exception.go b/internal/transport/router/exception.go index bd057c6..732151a 100644 --- a/internal/transport/router/exception.go +++ b/internal/transport/router/exception.go @@ -3,6 +3,7 @@ package router import ( "cybertron/internal/dependencies" "cybertron/internal/transport/handler" + "github.com/gin-gonic/gin" ) diff --git a/internal/transport/router/houston.go b/internal/transport/router/houston.go new file mode 100644 index 0000000..131aa14 --- /dev/null +++ b/internal/transport/router/houston.go @@ -0,0 +1,17 @@ +package router + +import ( + "cybertron/internal/dependencies" + + "github.com/gin-gonic/gin" +) + +func HoustonRouter(r *gin.Engine, dep *dependencies.Dependencies) { + hh := dep.Handler.HoustonHandler + houstonRouterGroup := r.Group("/api") + { + houstonRouterGroup.POST("/v1/create", hh.CreateHouston) + houstonRouterGroup.GET("/v1/products", hh.GetProducts) + houstonRouterGroup.GET("/v1/responderTeam", hh.GetResponderTeam) + } +} diff --git a/internal/transport/server.go b/internal/transport/server.go index 469c0f2..b2c2355 100644 --- a/internal/transport/server.go +++ b/internal/transport/server.go @@ -3,12 +3,13 @@ package transport import ( "cybertron/internal/transport/router" "fmt" - "github.com/gin-contrib/cors" "os" "os/signal" "syscall" "time" + "github.com/gin-contrib/cors" + "cybertron/configs" "cybertron/internal/dependencies" @@ -36,6 +37,7 @@ func (s *Server) router() { router.ReleasesRouter(s.gin, s.dependencies) router.ExceptionRouter(s.gin, s.dependencies) router.SearchRouter(s.gin, s.dependencies) + router.HoustonRouter(s.gin, s.dependencies) } func (s *Server) Start() { diff --git a/models/db/houston.go b/models/db/houston.go new file mode 100644 index 0000000..23ea561 --- /dev/null +++ b/models/db/houston.go @@ -0,0 +1,6 @@ +package db + +type Houston struct { + ID int `json:"id" gorm:"autoIncrement:true"` + houstonID string `json:"houstonID"` +} diff --git a/models/db/project.go b/models/db/project.go index 6f4d4b0..bef1353 100644 --- a/models/db/project.go +++ b/models/db/project.go @@ -13,4 +13,5 @@ type Project struct { Name string `json:"name" gorm:"unique"` Team string `json:"team"` Icon string `json:"icon"` + GithubUrl string `json:"githubUrl"` } diff --git a/pkg/houstonClient/houstonClient.go b/pkg/houstonClient/houstonClient.go new file mode 100644 index 0000000..f4a93c2 --- /dev/null +++ b/pkg/houstonClient/houstonClient.go @@ -0,0 +1,293 @@ +package houstonClient + +import ( + "bytes" + "cybertron/configs" + "cybertron/pkg/log" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/url" +) + +type HoustonClientInterface interface { + CreateIncident(payload CreateHoustonRequest) (*HoustonResponse, error) + GetAllProducts() (*ProductsResponse, error) + GetReportingAndResponder(productID []string) (*HoustonIncidentResponse, error) +} + +type HoustonClient struct { + baseUrl string + realmId string +} + +type ReportingTeam struct { + Value int `json:"value"` + Label string `json:"label"` +} + +type Product struct { + ProductID int `json:"product_id"` + ProductName string `json:"product_name"` +} + +type IncidentData struct { + ID int `json:"id"` + Title string `json:"title"` + Description string `json:"description"` + Status int `json:"status"` + StatusName string `json:"statusName"` + SeverityId int `json:"severityId"` + SeverityName string `json:"severityName"` + IncidentName string `json:"incidentName"` + SlackChannel string `json:"slackChannel"` + DetectionTime interface{} `json:"detectionTime"` + StartTime string `json:"startTime"` + EndTime interface{} `json:"endTime"` + TeamId int `json:"teamId"` + TeamName string `json:"teamName"` + JiraLinks interface{} `json:"jiraLinks"` + ConfluenceId interface{} `json:"confluenceId"` + SeverityTat string `json:"severityTat"` + RemindMeAt interface{} `json:"remindMeAt"` + EnableReminder bool `json:"enableReminder"` + CreatedBy string `json:"createdBy"` + UpdatedBy string `json:"updatedBy"` + CreatedAt string `json:"createdAt"` + UpdatedAt string `json:"updatedAt"` + RcaLink string `json:"rcaLink"` + ReportingTeam ReportingTeam `json:"reportingTeam"` + Products []Product `json:"products"` +} + +type HoustonResponse struct { + Data IncidentData `json:"data"` + Error json.RawMessage `json:"error"` + StatusCode int `json:"status"` +} + +type CreateHoustonRequest struct { + Title string `json:"title"` + SeverityId int `json:"severityId"` + Description string `json:"description"` + ReportingTeamId int `json:"reportingTeamId"` + ResponderTeamId int `json:"responderTeamId"` + ProductIds []int `json:"productIds"` + CreatedBy string `json:"createdBy"` +} + +type ProductsResponse struct { + Data ProductResponseData `json:"data"` + Error json.RawMessage `json:"error"` + StatusCode int `json:"status"` +} + +type ProductResponseData struct { + DefaultProduct DefaultProductType `json:"defaultProduct"` + Products []DefaultProductType `json:"products"` +} + +type DefaultProductType struct { + Label string `json:"label"` + Value json.Number `json:"value"` +} + +type Team struct { + Value int `json:"value"` + Label string `json:"label"` +} + +type TeamDataResponse struct { + DefaultTeam *Team `json:"defaultTeam"` // Use a pointer to handle possible `null` value + Teams []Team `json:"teams"` +} + +type IncidentTeamResponse struct { + ReportingTeam TeamDataResponse `json:"reportingTeam"` + ResponderTeam TeamDataResponse `json:"responderTeam"` +} + +type HoustonIncidentResponse struct { + Data IncidentTeamResponse `json:"data"` + Error json.RawMessage `json:"error"` + StatusCode int `json:"status"` +} + +func NewHoustonClient(houstonConfig configs.HoustonClientConfig) *HoustonClient { + return &HoustonClient{ + baseUrl: houstonConfig.GetHoustonBaseUrl(), + } +} + +var logger = log.Log.GetLog() +var client = http.Client{} + +const ( + SessionUrl = "%s/session/%s" +) + +func (m *HoustonClient) CreateIncident(payload CreateHoustonRequest) (*HoustonResponse, error) { + url := m.baseUrl + "/houston/create-incident-v3" + + fmt.Println("Creating incident with payload:", payload) + fmt.Println("POST URL:", url) + + // Marshal the payload to JSON + jsonPayload, err := json.Marshal(payload) + if err != nil { + return nil, err + } + + // Create a new POST request + req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonPayload)) + if err != nil { + return nil, err + } + + // Set the necessary headers + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Bearer "+m.realmId) + + // Use http.Client to send the request + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + // Check for unauthorized request + if resp.StatusCode == http.StatusUnauthorized { + return nil, errors.New("unauthorized request") + } + + // Read the response body + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + // Print the response body + fmt.Println("Response body:", string(body)) + + // Decode the response + var houstonResponse HoustonResponse + if err := json.Unmarshal(body, &houstonResponse); err != nil { + return nil, err + } + + houstonResponse.StatusCode = resp.StatusCode + fmt.Println("Incident created successfully with status code:", houstonResponse.StatusCode) + fmt.Println("Response data:", houstonResponse.Data) + return &houstonResponse, nil +} + +func (m *HoustonClient) GetAllProducts() (*ProductsResponse, error) { + url := m.baseUrl + "/houston/user/products" + + fmt.Println("GET URL:", url) + + // Create a new GET request + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + + // Set the necessary headers + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Bearer "+m.realmId) + + // Use http.Client to send the request + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + // Check for unauthorized request + if resp.StatusCode == http.StatusUnauthorized { + return nil, errors.New("unauthorized request") + } + + // Read the response body + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + // Print the response body + fmt.Println("Response body:", string(body)) + + // Decode the response + var productsResponse ProductsResponse + if err := json.Unmarshal(body, &productsResponse); err != nil { + return nil, err + } + + productsResponse.StatusCode = resp.StatusCode + fmt.Println("Products fetched successfully with status code:", productsResponse.StatusCode) + fmt.Println("Response data:", productsResponse.Data) + return &productsResponse, nil +} + +func (m *HoustonClient) GetReportingAndResponder(productID []string) (*HoustonIncidentResponse, error) { + baseURL := m.baseUrl + "/houston/product/reporting-and-responder-teams" + + // Construct URL with query parameters + u, err := url.Parse(baseURL) + if err != nil { + return nil, err + } + + q := u.Query() + for _, id := range productID { + q.Add("productID", id) + } + u.RawQuery = q.Encode() + + fmt.Println("GET URL:", u.String()) + + // Create a new GET request + req, err := http.NewRequest("GET", u.String(), nil) + if err != nil { + return nil, err + } + + // Set the necessary headers + + req.Header.Set("Content-Type", "application/json") + + // Use http.Client to send the request + resp, err := client.Do(req) + if err != nil { + fmt.Println("Error:", err) + } + defer resp.Body.Close() + + // Check for unauthorized request + if resp.StatusCode == http.StatusUnauthorized { + return nil, errors.New("unauthorized request") + } + + // Read the response body + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + // Print the response body + fmt.Println("Response body:", string(body)) + + // Decode the response + var productsResponse HoustonIncidentResponse + if err := json.Unmarshal(body, &productsResponse); err != nil { + return nil, err + } + + productsResponse.StatusCode = resp.StatusCode + fmt.Println("Products fetched successfully with status code:", productsResponse.StatusCode) + fmt.Println("Response data:", productsResponse.Data) + return &productsResponse, nil + +} diff --git a/service/HoustonService.go b/service/HoustonService.go new file mode 100644 index 0000000..a49c2d3 --- /dev/null +++ b/service/HoustonService.go @@ -0,0 +1,146 @@ +package service + +import ( + "cybertron/pkg/houstonClient" + "cybertron/pkg/kafka/producer" + "cybertron/pkg/log" + "fmt" + "net/http" + + "github.com/gin-gonic/gin" + "gorm.io/gorm" +) + +type HoustonService struct { + logger *log.Logger + dbClient *gorm.DB + kafkaProducer producer.KProducer + houstonClient houstonClient.HoustonClientInterface +} + +type CreateHoustonRequest struct { + Title string `json:"title"` + SeverityId int `json:"severityId"` + Description string `json:"description"` + ReportingTeamId int `json:"reportingTeamId"` + ResponderTeamId int `json:"responderTeamId"` + ProductIds []int `json:"productIds"` + CreatedBy string `json:"createdBy"` +} + +func NewHoustonService(logger *log.Logger, dbClient *gorm.DB, kafkaProducer producer.KProducer, houstonClient houstonClient.HoustonClientInterface) *HoustonService { + return &HoustonService{ + logger: logger, + dbClient: dbClient, + kafkaProducer: kafkaProducer, + houstonClient: houstonClient, + } +} + +func (houstonService *HoustonService) CreateHouston(c *gin.Context) { + var request CreateHoustonRequest + if err := c.BindJSON(&request); err != nil { + fmt.Println("Error binding JSON:", err) + createErrorResponse(c, http.StatusBadRequest, "Invalid request payload") + return + } + + fmt.Println("Received request payload:", request) + + if missingFields := validateCreateHoustonRequest(request); len(missingFields) > 0 { + fmt.Println("Missing required fields:", missingFields) + c.JSON(http.StatusBadRequest, gin.H{ + "message": "Missing required fields", + "fields": missingFields, + }) + return + } + + // Make the POST request using houstonClient + response, err := houstonService.houstonClient.CreateIncident(houstonClient.CreateHoustonRequest(request)) + if err != nil { + fmt.Println("Error creating incident:", err) + createErrorResponse(c, http.StatusInternalServerError, "Failed to create incident") + return + } + + // Handle the response + if response.StatusCode != http.StatusOK { + fmt.Println("Failed to create incident, status code:", response.StatusCode) + createErrorResponse(c, response.StatusCode, "Failed to create incident") + return + } + + // Send the response body back to the client + c.JSON(http.StatusBadRequest, gin.H{ + "message": "Incident created successfully", + "data": response.Data, + }) +} + +func (houstonService *HoustonService) GetProducts(c *gin.Context) { + // Get the products using the houstonClient + products, err := houstonService.houstonClient.GetAllProducts() + if err != nil { + fmt.Println("Error getting products:", err) + createErrorResponse(c, http.StatusInternalServerError, "Failed to get products") + return + } + + // Send the response back to the client + c.JSON(http.StatusOK, gin.H{ + "message": "Products fetched successfully", + "data": products.Data, + }) +} + +func (houstonService *HoustonService) GetResponderTeam(c *gin.Context) { + // read query params + productID := c.QueryArray("productID") + + // Get the responder team using the houstonClient + responderTeam, err := houstonService.houstonClient.GetReportingAndResponder(productID) + if err != nil { + fmt.Println("Error getting responder team:", err) + createErrorResponse(c, http.StatusInternalServerError, "Failed to get responder team") + return + } + + // Send the response back to the client + c.JSON(http.StatusOK, gin.H{ + "message": "Responder team fetched successfully", + "data": responderTeam.Data, + }) +} + +func validateCreateHoustonRequest(request CreateHoustonRequest) []string { + missingFields := []string{} + if request.Title == "" { + missingFields = append(missingFields, "title") + } + if request.SeverityId == 0 { + missingFields = append(missingFields, "severityId") + } + if request.Description == "" { + missingFields = append(missingFields, "description") + } + if request.ReportingTeamId == 0 { + missingFields = append(missingFields, "reportingTeamId") + } + if request.ResponderTeamId == 0 { + missingFields = append(missingFields, "responderTeamId") + } + if len(request.ProductIds) == 0 { + missingFields = append(missingFields, "productIds") + } + if request.CreatedBy == "" { + missingFields = append(missingFields, "createdBy") + } + return missingFields +} + +func createErrorResponse(c *gin.Context, statusCode int, message string) { + c.JSON(statusCode, gin.H{ + "message": message, + }) +} diff --git a/service/ProjectCreator.go b/service/ProjectCreator.go index 33e44cc..4dc6788 100644 --- a/service/ProjectCreator.go +++ b/service/ProjectCreator.go @@ -5,12 +5,13 @@ import ( "cybertron/models/db" "cybertron/pkg/kafka/producer" "cybertron/pkg/log" - "github.com/gin-gonic/gin" - "github.com/google/uuid" - "gorm.io/gorm" "math/big" "net/http" "strings" + + "github.com/gin-gonic/gin" + "github.com/google/uuid" + "gorm.io/gorm" ) type ProjectCreator struct { @@ -21,9 +22,10 @@ type ProjectCreator struct { } type ProjectBody struct { - Name string `json:"name" binding:"required"` - Team string `json:"team" binding:"required"` - Icon string `json:"icon"` + Name string `json:"name" binding:"required"` + Team string `json:"team" binding:"required"` + Icon string `json:"icon"` + GithubUrl string `json:"githubUrl" binding:"required"` } func NewProjectCreator(logger *log.Logger, dbClient *gorm.DB, s3Client *aws.Actions, kafkaProducer producer.KProducer) *ProjectCreator {