From 500941229114806bd511db5076e4756a37969d28 Mon Sep 17 00:00:00 2001 From: Blaize M Kaye Date: Thu, 19 Oct 2023 07:56:55 +1300 Subject: [PATCH 1/4] Adds /problems endpoint --- internal/defs.go | 25 ++++++++++ internal/service/service.go | 78 ++++++++++++++++++++++++++++++++ internal/service/service_test.go | 44 +++++++++++++++++- 3 files changed, 146 insertions(+), 1 deletion(-) diff --git a/internal/defs.go b/internal/defs.go index 5626cfd..ea508a3 100644 --- a/internal/defs.go +++ b/internal/defs.go @@ -20,3 +20,28 @@ type Facts struct { Facts []Fact `json:"facts"` Type string `json:"type"` } + +type ProblemSeverityRating string + +type Problem struct { + EnvironmentId string `json:"environment"` + Identifier string `json:"identifier"` + Version string `json:"version,omitempty"` + FixedVersion string `json:"fixedVersion,omitempty"` + Source string `json:"source,omitempty"` + Service string `json:"service,omitempty"` + Data string `json:"data"` + Severity ProblemSeverityRating `json:"severity,omitempty"` + SeverityScore float64 `json:"severityScore,omitempty"` + AssociatedPackage string `json:"associatedPackage,omitempty"` + Description string `json:"description,omitempty"` + Links string `json:"links,omitempty"` +} + +type Problems struct { + EnvironmentId int `json:"environment"` + ProjectName string `json:"projectName"` + EnvironmentName string `json:"environmentName"` + Problems []Problem `json:"problems"` + Type string `json:"type"` +} diff --git a/internal/service/service.go b/internal/service/service.go index dc000eb..73d6b03 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -28,9 +28,87 @@ func SetupRouter(secret string, messageQWriter func(data []byte) error, writeToQ r.MessageQWriter = messageQWriter r.WriteToQueue = writeToQueue router.POST("/facts", r.writeFacts) + router.POST("/problems", r.writeProblems) return router } +func (r *routerInstance) writeProblems(c *gin.Context) { + + h := &AuthHeader{} + if err := c.ShouldBindHeader(&h); err != nil { + c.JSON(http.StatusOK, err) + } + + namespace, err := tokens.ValidateAndExtractNamespaceDetailsFromToken(r.secret, h.Authorization) + + if err != nil { + c.JSON(http.StatusUnauthorized, gin.H{ + "status": "unauthorized", + "message": err.Error(), + }) + return + } + + fmt.Println("Going to write to namespace ", namespace) + + //TODO: drop "InsightsType" for Type of the form "direct.fact"/"direct.problem" + //details := &internal.Facts{Type: "direct.problems"} + details := &internal.Problems{Type: "direct.problems"} + + if err = c.ShouldBindJSON(details); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "status": "Unable to parse incoming data", + "message": err.Error(), + }) + fmt.Println(err) + return + } + + //let's force our facts to get pushed to the right place + lid, err := strconv.ParseInt(namespace.EnvironmentId, 10, 32) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "status": "Unable to parse environment ID", + "message": err.Error(), + }) + fmt.Println(err) + return + } + + details.EnvironmentId = int(lid) + details.ProjectName = namespace.ProjectName + details.EnvironmentName = namespace.EnvironmentName + for i := range details.Problems { + details.Problems[i].EnvironmentId = namespace.EnvironmentId + + if details.Problems[i].Source == "" { + details.Problems[i].Source = "InsightsRemoteWebService" + } + } + + // Write this to the queue + + jsonRep, err := json.Marshal(details) + if err != nil { + c.JSON(http.StatusInternalServerError, err) + return + } + + if r.WriteToQueue { + err = r.MessageQWriter(jsonRep) + if err != nil { + c.JSON(http.StatusInternalServerError, err) + return + } + } else { + fmt.Printf("Not writing to queue - would have sent these data %v\n", string(jsonRep)) + } + + c.JSON(http.StatusOK, gin.H{ + "message": "okay", + }) +} + func (r *routerInstance) writeFacts(c *gin.Context) { h := &AuthHeader{} diff --git a/internal/service/service_test.go b/internal/service/service_test.go index 544a12d..2d58f9a 100644 --- a/internal/service/service_test.go +++ b/internal/service/service_test.go @@ -29,7 +29,7 @@ func resetWriterOutput() { queueWriterOutput = "" } -func TestWriteRoute(t *testing.T) { +func TestWriteFactsRoute(t *testing.T) { defer resetWriterOutput() router := SetupRouter(secretTestTokenSecret, messageQueueWriter, true) w := httptest.NewRecorder() @@ -65,3 +65,45 @@ func TestWriteRoute(t *testing.T) { assert.Contains(t, w.Body.String(), w.Body.String()) assert.Contains(t, queueWriterOutput, testFacts.Facts[0].Name) } + +func TestWriteProblemsRoute(t *testing.T) { + defer resetWriterOutput() + router := SetupRouter(secretTestTokenSecret, messageQueueWriter, true) + w := httptest.NewRecorder() + + token, err := tokens.GenerateTokenForNamespace(secretTestTokenSecret, tokens.NamespaceDetails{ + Namespace: secretTestNamespace, + EnvironmentId: "1", + ProjectName: "Test", + EnvironmentName: "Test", + }) + + require.NoError(t, err) + + testFacts := internal.Problems{ + Problems: []internal.Problem{ + internal.Problem{ + EnvironmentId: "1", + Identifier: "123", + Version: "1", + FixedVersion: "2", + Source: "a unique sources", + Service: "test", + Data: "test", + Severity: "1", + SeverityScore: 1, + AssociatedPackage: "test", + Description: "test", + Links: "test", + }, + }} + bodyString, _ := json.Marshal(testFacts) + req, _ := http.NewRequest(http.MethodPost, "/problems", bytes.NewBuffer(bodyString)) + req.Header.Set("Authorization", token) + req.Header.Set("Content-Type", "application/json") + router.ServeHTTP(w, req) + + assert.Equal(t, http.StatusOK, w.Code) + assert.Contains(t, w.Body.String(), w.Body.String()) + assert.Contains(t, queueWriterOutput, testFacts.Problems[0].Source) +} From b816734350e12f67e39f33d9698ed54a041f50a4 Mon Sep 17 00:00:00 2001 From: Blaize M Kaye Date: Thu, 19 Oct 2023 09:59:32 +1300 Subject: [PATCH 2/4] Adds simple test --- internal/service/service_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/service_test.go b/internal/service/service_test.go index 2d58f9a..438b238 100644 --- a/internal/service/service_test.go +++ b/internal/service/service_test.go @@ -82,7 +82,7 @@ func TestWriteProblemsRoute(t *testing.T) { testFacts := internal.Problems{ Problems: []internal.Problem{ - internal.Problem{ + { EnvironmentId: "1", Identifier: "123", Version: "1", From aa04080051335dcbb0de47970ce022bba3e05903 Mon Sep 17 00:00:00 2001 From: Blaize M Kaye Date: Thu, 16 Nov 2023 10:22:23 +1300 Subject: [PATCH 3/4] Flattens facts --- internal/defs.go | 2 +- internal/service/service.go | 29 ++++++++---- internal/service/service_test.go | 81 +++++++++++++++++++++++--------- 3 files changed, 79 insertions(+), 33 deletions(-) diff --git a/internal/defs.go b/internal/defs.go index ea508a3..a8c8c58 100644 --- a/internal/defs.go +++ b/internal/defs.go @@ -24,7 +24,7 @@ type Facts struct { type ProblemSeverityRating string type Problem struct { - EnvironmentId string `json:"environment"` + EnvironmentId int `json:"environment"` Identifier string `json:"identifier"` Version string `json:"version,omitempty"` FixedVersion string `json:"fixedVersion,omitempty"` diff --git a/internal/service/service.go b/internal/service/service.go index 73d6b03..b3046cf 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -2,6 +2,7 @@ package service import ( "fmt" + "io/ioutil" "net/http" "strconv" @@ -54,8 +55,9 @@ func (r *routerInstance) writeProblems(c *gin.Context) { //TODO: drop "InsightsType" for Type of the form "direct.fact"/"direct.problem" //details := &internal.Facts{Type: "direct.problems"} details := &internal.Problems{Type: "direct.problems"} + problemList := *new([]internal.Problem) - if err = c.ShouldBindJSON(details); err != nil { + if err = c.ShouldBindJSON(&problemList); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "status": "Unable to parse incoming data", "message": err.Error(), @@ -78,8 +80,9 @@ func (r *routerInstance) writeProblems(c *gin.Context) { details.EnvironmentId = int(lid) details.ProjectName = namespace.ProjectName details.EnvironmentName = namespace.EnvironmentName + details.Problems = problemList for i := range details.Problems { - details.Problems[i].EnvironmentId = namespace.EnvironmentId + details.Problems[i].EnvironmentId = int(lid) if details.Problems[i].Source == "" { details.Problems[i].Source = "InsightsRemoteWebService" @@ -131,13 +134,21 @@ func (r *routerInstance) writeFacts(c *gin.Context) { //TODO: drop "InsightsType" for Type of the form "direct.fact"/"direct.problem" details := &internal.Facts{Type: "direct.facts"} - if err = c.ShouldBindJSON(details); err != nil { - c.JSON(http.StatusBadRequest, gin.H{ - "status": "Unable to parse incoming data", - "message": err.Error(), - }) - fmt.Println(err) - return + // we try two different ways of parsing incoming facts - first as a simple list of facts + ByteBody, _ := ioutil.ReadAll(c.Request.Body) + + factList := *new([]internal.Fact) + if err = json.Unmarshal(ByteBody, &factList); err != nil { // it might just be they're passing the "big" version with all details + if err = json.Unmarshal(ByteBody, details); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "status": "Unable to parse incoming data", + "message": err.Error(), + }) + fmt.Println(err) + return + } + } else { + details.Facts = factList } //let's force our facts to get pushed to the right place diff --git a/internal/service/service_test.go b/internal/service/service_test.go index 438b238..2c25425 100644 --- a/internal/service/service_test.go +++ b/internal/service/service_test.go @@ -16,11 +16,11 @@ import ( const secretTestTokenSecret = "secret" const secretTestNamespace = "testNS" +const testEnvironmentId = "777" var queueWriterOutput string func messageQueueWriter(data []byte) error { - //fmt.Println(string(data)) queueWriterOutput = string(data) return nil } @@ -36,7 +36,7 @@ func TestWriteFactsRoute(t *testing.T) { token, err := tokens.GenerateTokenForNamespace(secretTestTokenSecret, tokens.NamespaceDetails{ Namespace: secretTestNamespace, - EnvironmentId: "1", + EnvironmentId: testEnvironmentId, ProjectName: "Test", EnvironmentName: "Test", }) @@ -66,44 +66,79 @@ func TestWriteFactsRoute(t *testing.T) { assert.Contains(t, queueWriterOutput, testFacts.Facts[0].Name) } -func TestWriteProblemsRoute(t *testing.T) { +func TestWriteFactsRouteNoProjectData(t *testing.T) { defer resetWriterOutput() router := SetupRouter(secretTestTokenSecret, messageQueueWriter, true) w := httptest.NewRecorder() token, err := tokens.GenerateTokenForNamespace(secretTestTokenSecret, tokens.NamespaceDetails{ Namespace: secretTestNamespace, - EnvironmentId: "1", + EnvironmentId: testEnvironmentId, ProjectName: "Test", EnvironmentName: "Test", }) require.NoError(t, err) - testFacts := internal.Problems{ - Problems: []internal.Problem{ - { - EnvironmentId: "1", - Identifier: "123", - Version: "1", - FixedVersion: "2", - Source: "a unique sources", - Service: "test", - Data: "test", - Severity: "1", - SeverityScore: 1, - AssociatedPackage: "test", - Description: "test", - Links: "test", - }, - }} + testFacts := []internal.Fact{ + { + Name: "testfact1", + Value: "testvalue1", + Source: "testsource1", + Description: "testdescription1", + Type: "testtype1", + Category: "testcategory1", + Service: "testservice1", + }, + } bodyString, _ := json.Marshal(testFacts) - req, _ := http.NewRequest(http.MethodPost, "/problems", bytes.NewBuffer(bodyString)) + req, _ := http.NewRequest(http.MethodPost, "/facts", bytes.NewBuffer(bodyString)) req.Header.Set("Authorization", token) req.Header.Set("Content-Type", "application/json") router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) assert.Contains(t, w.Body.String(), w.Body.String()) - assert.Contains(t, queueWriterOutput, testFacts.Problems[0].Source) + assert.Contains(t, queueWriterOutput, testFacts[0].Name) +} + +func TestWriteProblemsRoute(t *testing.T) { + defer resetWriterOutput() + router := SetupRouter(secretTestTokenSecret, messageQueueWriter, true) + w := httptest.NewRecorder() + + token, err := tokens.GenerateTokenForNamespace(secretTestTokenSecret, tokens.NamespaceDetails{ + Namespace: secretTestNamespace, + EnvironmentId: testEnvironmentId, + ProjectName: "Test", + EnvironmentName: "Test", + }) + + require.NoError(t, err) + + testProblems := []internal.Problem{ + { + EnvironmentId: 4, + Identifier: "123", + Version: "1", + FixedVersion: "2", + Source: "a unique sources", + Service: "test", + Data: "test", + Severity: "1", + SeverityScore: 1, + AssociatedPackage: "test", + Description: "test", + Links: "test", + }, + } + bodyString, _ := json.Marshal(testProblems) + req, _ := http.NewRequest(http.MethodPost, "/problems", bytes.NewBuffer(bodyString)) + req.Header.Set("Authorization", token) + req.Header.Set("Content-Type", "application/json") + router.ServeHTTP(w, req) + + assert.Equal(t, http.StatusOK, w.Code) + + assert.Contains(t, queueWriterOutput, testProblems[0].Source) } From a29fdd6e32ffae33e53e0010eef9bd71a016f758 Mon Sep 17 00:00:00 2001 From: Blaize M Kaye Date: Thu, 16 Nov 2023 13:30:46 +1300 Subject: [PATCH 4/4] Removes extraneous comments --- internal/service/service.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/service/service.go b/internal/service/service.go index b3046cf..9af6d50 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -52,8 +52,6 @@ func (r *routerInstance) writeProblems(c *gin.Context) { fmt.Println("Going to write to namespace ", namespace) - //TODO: drop "InsightsType" for Type of the form "direct.fact"/"direct.problem" - //details := &internal.Facts{Type: "direct.problems"} details := &internal.Problems{Type: "direct.problems"} problemList := *new([]internal.Problem)