feat: Make maximum number of results and events configurable (#1110)
This commit is contained in:
@@ -126,6 +126,6 @@ func (a *API) createRouter(cfg *config.Config) *fiber.App {
|
||||
}
|
||||
}
|
||||
protectedAPIRouter.Get("/v1/endpoints/statuses", EndpointStatuses(cfg))
|
||||
protectedAPIRouter.Get("/v1/endpoints/:key/statuses", EndpointStatus)
|
||||
protectedAPIRouter.Get("/v1/endpoints/:key/statuses", EndpointStatus(cfg))
|
||||
return app
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
// Due to how intensive this operation can be on the storage, this function leverages a cache.
|
||||
func EndpointStatuses(cfg *config.Config) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
page, pageSize := extractPageAndPageSizeFromRequest(c)
|
||||
page, pageSize := extractPageAndPageSizeFromRequest(c, cfg.Storage.MaximumNumberOfResults)
|
||||
value, exists := cache.Get(fmt.Sprintf("endpoint-status-%d-%d", page, pageSize))
|
||||
var data []byte
|
||||
if !exists {
|
||||
@@ -83,25 +83,27 @@ func getEndpointStatusesFromRemoteInstances(remoteConfig *remote.Config) ([]*end
|
||||
}
|
||||
|
||||
// EndpointStatus retrieves a single endpoint.Status by group and endpoint name
|
||||
func EndpointStatus(c *fiber.Ctx) error {
|
||||
page, pageSize := extractPageAndPageSizeFromRequest(c)
|
||||
endpointStatus, err := store.Get().GetEndpointStatusByKey(c.Params("key"), paging.NewEndpointStatusParams().WithResults(page, pageSize).WithEvents(1, common.MaximumNumberOfEvents))
|
||||
if err != nil {
|
||||
if errors.Is(err, common.ErrEndpointNotFound) {
|
||||
return c.Status(404).SendString(err.Error())
|
||||
func EndpointStatus(cfg *config.Config) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
page, pageSize := extractPageAndPageSizeFromRequest(c, cfg.Storage.MaximumNumberOfResults)
|
||||
endpointStatus, err := store.Get().GetEndpointStatusByKey(c.Params("key"), paging.NewEndpointStatusParams().WithResults(page, pageSize).WithEvents(1, cfg.Storage.MaximumNumberOfEvents))
|
||||
if err != nil {
|
||||
if errors.Is(err, common.ErrEndpointNotFound) {
|
||||
return c.Status(404).SendString(err.Error())
|
||||
}
|
||||
logr.Errorf("[api.EndpointStatus] Failed to retrieve endpoint status: %s", err.Error())
|
||||
return c.Status(500).SendString(err.Error())
|
||||
}
|
||||
logr.Errorf("[api.EndpointStatus] Failed to retrieve endpoint status: %s", err.Error())
|
||||
return c.Status(500).SendString(err.Error())
|
||||
if endpointStatus == nil { // XXX: is this check necessary?
|
||||
logr.Errorf("[api.EndpointStatus] Endpoint with key=%s not found", c.Params("key"))
|
||||
return c.Status(404).SendString("not found")
|
||||
}
|
||||
output, err := json.Marshal(endpointStatus)
|
||||
if err != nil {
|
||||
logr.Errorf("[api.EndpointStatus] Unable to marshal object to JSON: %s", err.Error())
|
||||
return c.Status(500).SendString("unable to marshal object to JSON")
|
||||
}
|
||||
c.Set("Content-Type", "application/json")
|
||||
return c.Status(200).Send(output)
|
||||
}
|
||||
if endpointStatus == nil { // XXX: is this check necessary?
|
||||
logr.Errorf("[api.EndpointStatus] Endpoint with key=%s not found", c.Params("key"))
|
||||
return c.Status(404).SendString("not found")
|
||||
}
|
||||
output, err := json.Marshal(endpointStatus)
|
||||
if err != nil {
|
||||
logr.Errorf("[api.EndpointStatus] Unable to marshal object to JSON: %s", err.Error())
|
||||
return c.Status(500).SendString("unable to marshal object to JSON")
|
||||
}
|
||||
c.Set("Content-Type", "application/json")
|
||||
return c.Status(200).Send(output)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/TwiN/gatus/v5/config"
|
||||
"github.com/TwiN/gatus/v5/config/endpoint"
|
||||
"github.com/TwiN/gatus/v5/storage"
|
||||
"github.com/TwiN/gatus/v5/storage/store"
|
||||
"github.com/TwiN/gatus/v5/watchdog"
|
||||
)
|
||||
@@ -95,6 +96,10 @@ func TestEndpointStatus(t *testing.T) {
|
||||
Group: "core",
|
||||
},
|
||||
},
|
||||
Storage: &storage.Config{
|
||||
MaximumNumberOfResults: storage.DefaultMaximumNumberOfResults,
|
||||
MaximumNumberOfEvents: storage.DefaultMaximumNumberOfEvents,
|
||||
},
|
||||
}
|
||||
watchdog.UpdateEndpointStatuses(cfg.Endpoints[0], &endpoint.Result{Success: true, Duration: time.Millisecond, Timestamp: time.Now()})
|
||||
watchdog.UpdateEndpointStatuses(cfg.Endpoints[1], &endpoint.Result{Success: false, Duration: time.Second, Timestamp: time.Now()})
|
||||
@@ -156,7 +161,13 @@ func TestEndpointStatuses(t *testing.T) {
|
||||
// Can't be bothered dealing with timezone issues on the worker that runs the automated tests
|
||||
firstResult.Timestamp = time.Time{}
|
||||
secondResult.Timestamp = time.Time{}
|
||||
api := New(&config.Config{Metrics: true})
|
||||
api := New(&config.Config{
|
||||
Metrics: true,
|
||||
Storage: &storage.Config{
|
||||
MaximumNumberOfResults: storage.DefaultMaximumNumberOfResults,
|
||||
MaximumNumberOfEvents: storage.DefaultMaximumNumberOfEvents,
|
||||
},
|
||||
})
|
||||
router := api.Router()
|
||||
type Scenario struct {
|
||||
Name string
|
||||
|
||||
10
api/util.go
10
api/util.go
@@ -3,7 +3,6 @@ package api
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/TwiN/gatus/v5/storage/store/common"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
@@ -13,12 +12,9 @@ const (
|
||||
|
||||
// DefaultPageSize is the default page siZE to use if none is specified or an invalid value is provided
|
||||
DefaultPageSize = 20
|
||||
|
||||
// MaximumPageSize is the maximum page size allowed
|
||||
MaximumPageSize = common.MaximumNumberOfResults
|
||||
)
|
||||
|
||||
func extractPageAndPageSizeFromRequest(c *fiber.Ctx) (page, pageSize int) {
|
||||
func extractPageAndPageSizeFromRequest(c *fiber.Ctx, maximumNumberOfResults int) (page, pageSize int) {
|
||||
var err error
|
||||
if pageParameter := c.Query("page"); len(pageParameter) == 0 {
|
||||
page = DefaultPage
|
||||
@@ -38,8 +34,8 @@ func extractPageAndPageSizeFromRequest(c *fiber.Ctx) (page, pageSize int) {
|
||||
if err != nil {
|
||||
pageSize = DefaultPageSize
|
||||
}
|
||||
if pageSize > MaximumPageSize {
|
||||
pageSize = MaximumPageSize
|
||||
if pageSize > maximumNumberOfResults {
|
||||
pageSize = maximumNumberOfResults
|
||||
} else if pageSize < 1 {
|
||||
pageSize = DefaultPageSize
|
||||
}
|
||||
|
||||
@@ -4,54 +4,62 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/TwiN/gatus/v5/storage"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
func TestExtractPageAndPageSizeFromRequest(t *testing.T) {
|
||||
type Scenario struct {
|
||||
Name string
|
||||
Page string
|
||||
PageSize string
|
||||
ExpectedPage int
|
||||
ExpectedPageSize int
|
||||
Name string
|
||||
Page string
|
||||
PageSize string
|
||||
ExpectedPage int
|
||||
ExpectedPageSize int
|
||||
MaximumNumberOfResults int
|
||||
}
|
||||
scenarios := []Scenario{
|
||||
{
|
||||
Page: "1",
|
||||
PageSize: "20",
|
||||
ExpectedPage: 1,
|
||||
ExpectedPageSize: 20,
|
||||
Page: "1",
|
||||
PageSize: "20",
|
||||
ExpectedPage: 1,
|
||||
ExpectedPageSize: 20,
|
||||
MaximumNumberOfResults: 20,
|
||||
},
|
||||
{
|
||||
Page: "2",
|
||||
PageSize: "10",
|
||||
ExpectedPage: 2,
|
||||
ExpectedPageSize: 10,
|
||||
Page: "2",
|
||||
PageSize: "10",
|
||||
ExpectedPage: 2,
|
||||
ExpectedPageSize: 10,
|
||||
MaximumNumberOfResults: 40,
|
||||
},
|
||||
{
|
||||
Page: "2",
|
||||
PageSize: "10",
|
||||
ExpectedPage: 2,
|
||||
ExpectedPageSize: 10,
|
||||
Page: "2",
|
||||
PageSize: "10",
|
||||
ExpectedPage: 2,
|
||||
ExpectedPageSize: 10,
|
||||
MaximumNumberOfResults: 200,
|
||||
},
|
||||
{
|
||||
Page: "1",
|
||||
PageSize: "999999",
|
||||
ExpectedPage: 1,
|
||||
ExpectedPageSize: MaximumPageSize,
|
||||
Page: "1",
|
||||
PageSize: "999999",
|
||||
ExpectedPage: 1,
|
||||
ExpectedPageSize: storage.DefaultMaximumNumberOfResults,
|
||||
MaximumNumberOfResults: 100,
|
||||
},
|
||||
{
|
||||
Page: "-1",
|
||||
PageSize: "-1",
|
||||
ExpectedPage: DefaultPage,
|
||||
ExpectedPageSize: DefaultPageSize,
|
||||
Page: "-1",
|
||||
PageSize: "-1",
|
||||
ExpectedPage: DefaultPage,
|
||||
ExpectedPageSize: DefaultPageSize,
|
||||
MaximumNumberOfResults: 20,
|
||||
},
|
||||
{
|
||||
Page: "invalid",
|
||||
PageSize: "invalid",
|
||||
ExpectedPage: DefaultPage,
|
||||
ExpectedPageSize: DefaultPageSize,
|
||||
Page: "invalid",
|
||||
PageSize: "invalid",
|
||||
ExpectedPage: DefaultPage,
|
||||
ExpectedPageSize: DefaultPageSize,
|
||||
MaximumNumberOfResults: 100,
|
||||
},
|
||||
}
|
||||
for _, scenario := range scenarios {
|
||||
@@ -61,7 +69,7 @@ func TestExtractPageAndPageSizeFromRequest(t *testing.T) {
|
||||
c := app.AcquireCtx(&fasthttp.RequestCtx{})
|
||||
defer app.ReleaseCtx(c)
|
||||
c.Request().SetRequestURI(fmt.Sprintf("/api/v1/statuses?page=%s&pageSize=%s", scenario.Page, scenario.PageSize))
|
||||
actualPage, actualPageSize := extractPageAndPageSizeFromRequest(c)
|
||||
actualPage, actualPageSize := extractPageAndPageSizeFromRequest(c, scenario.MaximumNumberOfResults)
|
||||
if actualPage != scenario.ExpectedPage {
|
||||
t.Errorf("expected %d, got %d", scenario.ExpectedPage, actualPage)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user