Public source validation / validate (push) Failing after 3m8s
87 lines
2.8 KiB
Go
87 lines
2.8 KiB
Go
package eventapi
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/itworx/pulse/internal/auth"
|
|
"github.com/itworx/pulse/internal/problem"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
)
|
|
|
|
type Event struct {
|
|
ID string `json:"id"`
|
|
Type string `json:"type"`
|
|
Severity string `json:"severity"`
|
|
EntityID *string `json:"entityId,omitempty"`
|
|
SourceID *string `json:"sourceId,omitempty"`
|
|
OccurredAt time.Time `json:"occurredAt"`
|
|
ReceivedAt time.Time `json:"receivedAt"`
|
|
Summary string `json:"summary"`
|
|
Attributes json.RawMessage `json:"attributes"`
|
|
CorrelationID *string `json:"correlationId,omitempty"`
|
|
}
|
|
|
|
type Store interface {
|
|
List(context.Context, int) ([]Event, error)
|
|
}
|
|
|
|
type PostgresStore struct{ Pool *pgxpool.Pool }
|
|
|
|
func (s PostgresStore) List(ctx context.Context, limit int) ([]Event, error) {
|
|
if s.Pool == nil {
|
|
return nil, errors.New("event database pool is nil")
|
|
}
|
|
rows, err := s.Pool.Query(ctx, `SELECT id::text,event_type,severity,entity_id::text,source_id::text,occurred_at,received_at,summary,attributes,correlation_id FROM events ORDER BY occurred_at DESC,id DESC LIMIT $1`, limit)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := make([]Event, 0, limit)
|
|
for rows.Next() {
|
|
var item Event
|
|
if err := rows.Scan(&item.ID, &item.Type, &item.Severity, &item.EntityID, &item.SourceID, &item.OccurredAt, &item.ReceivedAt, &item.Summary, &item.Attributes, &item.CorrelationID); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, item)
|
|
}
|
|
return items, rows.Err()
|
|
}
|
|
|
|
type Handler struct{ Store Store }
|
|
|
|
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet || r.URL.Path != "/api/v1/events" {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
if _, ok := auth.PrincipalFromContext(r.Context()); !ok {
|
|
problem.Write(w, r, http.StatusUnauthorized, "UNAUTHORIZED", "Authentication required", "Authentication is required to read events.", nil)
|
|
return
|
|
}
|
|
limit := 100
|
|
if raw := r.URL.Query().Get("limit"); raw != "" {
|
|
value, err := strconv.Atoi(raw)
|
|
if err != nil || value < 1 || value > 100 {
|
|
problem.Write(w, r, http.StatusBadRequest, "INVALID_LIMIT", "Invalid limit", "The event limit must be between 1 and 100.", nil)
|
|
return
|
|
}
|
|
limit = value
|
|
}
|
|
items, err := h.Store.List(r.Context(), limit)
|
|
if err != nil {
|
|
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
|
|
return
|
|
}
|
|
problem.Write(w, r, http.StatusServiceUnavailable, "EVENTS_UNAVAILABLE", "Events unavailable", "Gebeurtenissen konden niet worden gelezen.", nil)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Header().Set("Cache-Control", "private, max-age=5")
|
|
_ = json.NewEncoder(w).Encode(map[string]any{"items": items})
|
|
}
|