Skip to content

Commit

Permalink
Adds deletion endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
bomoko committed Dec 14, 2023
1 parent a5221c5 commit 929be1a
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 6 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,21 @@ There are several parts to this - but from a user perspective it is fairly strai

How this all works is coordinated across a few subsystems.

#### Clearing facts and problems

There are cases where clearing facts/problems for an environment _without_ writing a new set of insights.

This is done by issuing a DELETE method call (with the authentication token in the header) to one of the following four endpoints

* `http://lagoon-remote-insights-remote.lagoon.svc/facts/{SOURCE}`
* `http://lagoon-remote-insights-remote.lagoon.svc/problems/{SOURCE}`
* `http://lagoon-remote-insights-remote.lagoon.svc/facts/{SOURCE}/{SERVICE}`
* `http://lagoon-remote-insights-remote.lagoon.svc/problems/{SOURCE}/{SERVICE}`

Where `SOURCE` and `SERVICE` target the appropriate categorizations used generating the insights.

Essentially these calls correspond to the "deleteProblemsFromSource" and "deleteFactsFromSource" Lagoon API calls

#### Authorization Token

The Authorization token is a JWT that is generated per project and environment by the insights-remote [namespace controller](controllers/namespace_controller.go)
Expand Down
15 changes: 15 additions & 0 deletions internal/service/defs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package service

type DeleteFactsMessage struct {
Type string `json:"type"`
EnvironmentId int `json:"environmentId"`
Source string `json:"source"`
Service string `json:"service"`
}

type DeleteProblemsMessage struct {
Type string `json:"type"`
EnvironmentId int `json:"environmentId"`
Source string `json:"source"`
Service string `json:"service"`
}
85 changes: 79 additions & 6 deletions internal/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,82 @@ type routerInstance struct {
WriteToQueue bool
}

const deleteFactsType = "direct.delete.facts"
const deleteProblemsType = "direct.delete.problems"

func SetupRouter(secret string, messageQWriter func(data []byte) error, writeToQueue bool) *gin.Engine {
router := gin.Default()
r := routerInstance{secret: secret}
r.MessageQWriter = messageQWriter
r.WriteToQueue = writeToQueue
router.POST("/facts", r.writeFacts)
router.POST("/problems", r.writeProblems)
router.DELETE("/problems/:source", r.deleteProblems)
router.DELETE("/problems/:source/:service", r.deleteProblems)
router.DELETE("/facts/:source", r.deleteFacts)
router.DELETE("/facts/:source/:service", r.deleteFacts)
return router
}

func (r *routerInstance) deleteProblems(c *gin.Context) {
generateDeletionMessage(c, r, deleteProblemsType)
}

func (r *routerInstance) deleteFacts(c *gin.Context) {
generateDeletionMessage(c, r, deleteFactsType)
}

func generateDeletionMessage(c *gin.Context, r *routerInstance, deletionType string) {
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
}

source := c.Params.ByName("source")
service := c.Params.ByName("service")

envid, err := strconv.ParseInt(namespace.EnvironmentId, 10, 32)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"status": "BAD REQUEST",
"message": err.Error(),
})
return
}

message := DeleteFactsMessage{
Type: deletionType,
EnvironmentId: int(envid),
Source: source,
Service: service,
}

jsonRep, err := json.Marshal(message)
if err != nil {
c.JSON(http.StatusInternalServerError, err)
return
}

if err := r.writeToQueue(c, err, jsonRep); err != nil {
c.JSON(http.StatusInternalServerError, err)
return
}

c.JSON(http.StatusOK, gin.H{
"message": "okay",
})
}

func (r *routerInstance) writeProblems(c *gin.Context) {

h := &AuthHeader{}
Expand Down Expand Up @@ -95,19 +161,26 @@ func (r *routerInstance) writeProblems(c *gin.Context) {
return
}

if err := r.writeToQueue(c, err, jsonRep); err != nil {
c.JSON(http.StatusInternalServerError, err)
return
}

c.JSON(http.StatusOK, gin.H{
"message": "okay",
})
}

func (r *routerInstance) writeToQueue(c *gin.Context, err error, jsonRep []byte) error {
if r.WriteToQueue {
err = r.MessageQWriter(jsonRep)
if err != nil {
c.JSON(http.StatusInternalServerError, err)
return
return err
}
} else {
fmt.Printf("Not writing to queue - would have sent these data %v\n", string(jsonRep))
}

c.JSON(http.StatusOK, gin.H{
"message": "okay",
})
return nil
}

func (r *routerInstance) writeFacts(c *gin.Context) {
Expand Down
46 changes: 46 additions & 0 deletions internal/service/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,49 @@ func TestWriteProblemsRoute(t *testing.T) {

assert.Contains(t, queueWriterOutput, testProblems[0].Source)
}

func TestFactDeletionRoute(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)

var bodyString []byte
req, _ := http.NewRequest(http.MethodDelete, "/facts/testsource", 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)
}

func TestProblemDeletionRoute(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)

var bodyString []byte
req, _ := http.NewRequest(http.MethodDelete, "/problems/testsource", 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)
}

0 comments on commit 929be1a

Please sign in to comment.