Skip to content

Commit

Permalink
Feature/174 status api (#175)
Browse files Browse the repository at this point in the history
  • Loading branch information
MaartendeKruijf committed Jun 20, 2024
1 parent b185e91 commit c3484ee
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 101 deletions.
111 changes: 34 additions & 77 deletions docs/content/en/docs/core-components/api-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -417,80 +417,54 @@ None
@startjson
{
"version": "1.0.0",
"components": [
{
"name": "Component name",
"status": "ready/running/failed/stopped/...",
"message": "Some message",
"version": "semver verison: 1.0.0"
}
]
"runtime": "docker/windows/linux/macos/other",
"mode" : "development/production",
"time" : "2020-03-04T15:56:00.123456Z",
"uptime": {
"since": "2020-03-04T15:56:00.123456Z",
"milis": "uptime in miliseconds"
}
}
@endjson
```

##### Error
5XX/Internal error, 500/503/504 message.

---
----

#### GET `/status/fins` | not implemented
Call this endpoint to see if SOARCA Fins are up and ready. This call has no payload body.

#### GET /status/playbook
Get all running playbooks
##### Call payload
None

##### Response
200/OK

```plantuml
@startjson
{
"playbooks": [
{"type": "playbook",
"spec_version": "cacao-2.0",
"id": "playbook--91220064-3c6f-4b58-99e9-196e64f9bde7",
"name": "SOARCA Main Flow",
"description": "This playbook will run for each trigger event in SOARCA",
"playbook_types": ["notification"],
"created_by": "identity--06d8f218-f4e9-4f9f-9108-501de03d419f",
"created": "2020-03-04T15:56:00.123456Z",
"modified": "2020-03-04T15:56:00.123456Z",
"revoked": false,
"valid_from": "2020-03-04T15:56:00.123456Z",
"valid_until": "2020-07-31T23:59:59.999999Z",
"derived_from": [],
"priority": 1,
"severity": 1,
"impact": 1}
]
"fins": [
{
"name": "Fin name",
"status": "ready/running/failed/stopped/...",
"id": "The fin UUID",
"version": "semver verison: 1.0.0"
}
]
}
@endjson
```

##### Error
400/BAD REQUEST general error on error.

----

#### GET `/status/playbook/xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx (playbook-id)`
Get playbook details of running a playbook. It will return CACAO playbook JSON

##### Call payload
None

##### Response
200/OK

See [cacao playbook JSON](#cacao-playbook-json)
Empty payload if no playbooks are running
5XX/Internal error, 500/503/504 message.

##### Error
400/BAD REQUEST general error on error.

----

#### GET `/status/playbook/{playbook-id}`
Get course of action list for coa awaiting action.
#### GET `/status/reporters` | not implemented
Call this endpoint to see which SOARCA reportes are used. This call has no payload body.

##### Call payload
None
Expand All @@ -501,48 +475,31 @@ None
```plantuml
@startjson
{
"actions": [
{
"playbook_id": "playbook--91220064-3c6f-4b58-99e9-196e64f9bde7",
"status": "running/finished/failed/stopped/paused"
}
]
"reporters": [
{
"name": "Reporter name"
}
]
}
@endjson
```

##### Error
400/BAD REQUEST general error on error.
5XX/Internal error, 500/503/504 message.

----

---

#### GET /status/history
Get all playbook ids and statuses that have been run excluded those that are running or paused.
#### GET `/status/ping`
See if SOARCA is up this will only return if all SOARCA services are ready

##### Call payload
None

##### Response
200/OK

```plantuml
@startjson
{
"actions": [
{
"playbook_id": "playbook--91220064-3c6f-4b58-99e9-196e64f9bde7",
"status": "running/finished/failed/stopped/paused"
}
]
}
@endjson
```

##### Error
400/BAD REQUEST general error on error.

`pong`

## Usage example flow

Expand Down
7 changes: 5 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"soarca/internal/controller"
"soarca/logger"
"soarca/routes/status"
"soarca/swaggerdocs"
"soarca/utils"

Expand Down Expand Up @@ -35,8 +36,8 @@ const banner = `
`

// @title SOARCA API
// @version 1.0.0
// @title SOARCA API
// @version 1.0.0
func main() {
fmt.Print(banner)
log.Info("Version: ", Version)
Expand All @@ -49,6 +50,8 @@ func main() {
Host = "localhost:" + utils.GetEnv("PORT", "8080")
swaggerdocs.SwaggerInfo.Host = Host

// Version is only available here
status.SetVersion(Version)
errinit := controller.Initialize()
if errinit != nil {
log.Fatal("Something Went wrong with setting-up the app, msg: ", errinit)
Expand Down
2 changes: 0 additions & 2 deletions models/api/reporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import (
"time"
)

type Status uint8

// Reporter model adapted from https://github.com/cyentific-rni/workflow-status/blob/main/README.md

const (
Expand Down
16 changes: 16 additions & 0 deletions models/api/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package api

import "time"

type Uptime struct {
Since time.Time `json:"since"`
Milliseconds uint64 `json:"milliseconds"`
}

type Status struct {
Version string `json:"version"`
Runtime string `json:"runtime"`
Mode string `json:"mode"`
Time time.Time `json:"time"`
Uptime Uptime `json:"uptime"`
}
38 changes: 38 additions & 0 deletions routes/status/status_api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package status

import (
"net/http"
"runtime"
"soarca/models/api"
"soarca/utils"
"time"

"github.com/gin-gonic/gin"
)

var status = api.Status{Uptime: api.Uptime{Since: time.Now(), Milliseconds: 0},
Mode: utils.GetEnv("LOG_MODE", "production"),
Runtime: runtime.GOOS}

func SetVersion(version string) {
status.Version = version
}

// /Status GET handler for handling status api calls
// Returns the status model object for SOARCA
//
// @Summary gets the SOARCA status
// @Schemes
// @Description return SOARCA status
// @Tags status
// @Produce json
// @success 200 {object} api.Status
// @failure 400 {object} api.Error
// @Router /status [GET]
func Api(g *gin.Context) {

status.Uptime.Milliseconds = uint64(time.Since(status.Uptime.Since).Milliseconds())
status.Time = time.Now()

g.JSON(http.StatusOK, status)
}
38 changes: 18 additions & 20 deletions routes/status/status_endpoints.go
Original file line number Diff line number Diff line change
@@ -1,34 +1,32 @@
package coa
package status

import (
"fmt"
"net/http"

"github.com/gin-gonic/gin"
)

func Helloworld(g *gin.Context) {
g.JSON(http.StatusOK, "helloworld from /status")
}

func id_tester(g *gin.Context) {
// Get the value of the 'id' parameter from the URL
id := g.Param("id")
fmt.Println(id)
// /Status/ping GET handler for handling status api calls
// Returns the status model object for SOARCA
//
// @Summary ping to see if SOARCA is up returns pong
// @Schemes
// @Description return SOARCA status
// @Tags ping pong
// @Produce plain
// @success 200 string pong
// @Router /status/ping [GET]
func Pong(g *gin.Context) {
g.Data(http.StatusOK, "text/plain", []byte("pong"))
}

// GET /status
// GET /status/playbook
// GET /status/playbook/id
// GET /status/coa/id
// GET /status/history
// GET /status/ping
func Routes(route *gin.Engine) {
coa := route.Group("/status")
router := route.Group("/status")
{
coa.GET("/", Helloworld)
coa.GET("/playbook/:id", id_tester)
coa.GET("/coa/:id", id_tester)
coa.GET("/history", Helloworld)
// workflow.POST()
router.GET("/", Api)
router.GET("/ping", Pong)

}
}
73 changes: 73 additions & 0 deletions test/integration/api/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ package api_test

import (
"bytes"
"encoding/json"
"io"
"net/http"
"soarca/internal/controller"
"soarca/models/api"
"testing"
"time"

Expand Down Expand Up @@ -89,3 +92,73 @@ func TestCorsHeaderFromNonAllowedOrigin(t *testing.T) {
assert.Equal(t, http.StatusForbidden, response2.StatusCode)

}

func TestPingPong(t *testing.T) {
// Start SOARCA in separate threat
t.Setenv("PORT", "8082")
go initializeSoarca(t)

// Wait for the server to be online
time.Sleep(400 * time.Millisecond)

client := http.Client{}
buffer := bytes.NewBufferString("")
request, err := http.NewRequest("GET", "http://localhost:8082/status/ping", buffer)
if err != nil {
t.Fail()
}

// request.Header.Add("Origin", "http://example.com")
response, err := client.Do(request)
t.Log(response)
if err != nil {
t.Log(err)
t.Fail()
}

assert.Equal(t, http.StatusOK, response.StatusCode)
defer response.Body.Close()
body, err := io.ReadAll(response.Body)
assert.Equal(t, nil, err)

assert.Equal(t, "pong", string(body))

}

func TestStatus(t *testing.T) {
// Start SOARCA in separate threat
t.Setenv("PORT", "8083")
go initializeSoarca(t)

// Wait for the server to be online
time.Sleep(400 * time.Millisecond)

client := http.Client{}
buffer := bytes.NewBufferString("")
request, err := http.NewRequest("GET", "http://localhost:8083/status", buffer)
if err != nil {
t.Fail()
}

response, err := client.Do(request)
if err != nil {
t.Log(err)
t.Fail()
}

assert.Equal(t, http.StatusOK, response.StatusCode)
defer response.Body.Close()
body, err := io.ReadAll(response.Body)
assert.Equal(t, nil, err)

status := api.Status{}
err = json.Unmarshal(body, &status)
assert.Equal(t, nil, err)

assert.Equal(t, "production", status.Mode)
assert.NotEmpty(t, status.Mode)
assert.NotEmpty(t, status.Runtime)
assert.NotEmpty(t, status.Time)
t.Log(status)

}

0 comments on commit c3484ee

Please sign in to comment.