mirror of
https://github.com/EZ-Api/ez-api.git
synced 2026-01-13 17:47:51 +00:00
feat(api): add API key stats flush and summary endpoints
Introduce internal endpoint for flushing accumulated APIKey statistics from data plane to control plane database, updating both individual API keys and their parent provider groups with request counts and success/failure rates. Add admin endpoint to retrieve aggregated API key statistics summary across all provider groups, including total requests, success/failure counts, and calculated rates.
This commit is contained in:
@@ -2,6 +2,8 @@ package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
@@ -102,3 +104,79 @@ func TestInternalHandler_FlushStatsUpdatesCounters(t *testing.T) {
|
||||
t.Fatalf("key2 last_accessed_at: got=%v", got2.LastAccessedAt)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInternalHandler_FlushAPIKeyStatsUpdatesCounters(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
db, err := gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{})
|
||||
if err != nil {
|
||||
t.Fatalf("open sqlite: %v", err)
|
||||
}
|
||||
if err := db.AutoMigrate(&model.ProviderGroup{}, &model.APIKey{}); err != nil {
|
||||
t.Fatalf("migrate: %v", err)
|
||||
}
|
||||
|
||||
group := model.ProviderGroup{Name: "g1", Type: "openai", BaseURL: "https://example.com"}
|
||||
if err := db.Create(&group).Error; err != nil {
|
||||
t.Fatalf("create group: %v", err)
|
||||
}
|
||||
key1 := model.APIKey{GroupID: group.ID, APIKey: "k1", Status: "active"}
|
||||
key2 := model.APIKey{GroupID: group.ID, APIKey: "k2", Status: "active"}
|
||||
if err := db.Create(&key1).Error; err != nil {
|
||||
t.Fatalf("create key1: %v", err)
|
||||
}
|
||||
if err := db.Create(&key2).Error; err != nil {
|
||||
t.Fatalf("create key2: %v", err)
|
||||
}
|
||||
|
||||
handler := NewInternalHandler(db)
|
||||
r := gin.New()
|
||||
r.POST("/internal/apikey-stats/flush", handler.FlushAPIKeyStats)
|
||||
|
||||
body := []byte(`{
|
||||
"keys": [
|
||||
{"api_key_id": ` + fmt.Sprint(key1.ID) + `, "requests": 5, "success_requests": 3},
|
||||
{"api_key_id": ` + fmt.Sprint(key2.ID) + `, "requests": 4, "success_requests": 4}
|
||||
]
|
||||
}`)
|
||||
req := httptest.NewRequest(http.MethodPost, "/internal/apikey-stats/flush", bytes.NewReader(body))
|
||||
rec := httptest.NewRecorder()
|
||||
r.ServeHTTP(rec, req)
|
||||
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("unexpected status: got=%d body=%s", rec.Code, rec.Body.String())
|
||||
}
|
||||
|
||||
var got1 model.APIKey
|
||||
if err := db.First(&got1, key1.ID).Error; err != nil {
|
||||
t.Fatalf("load key1: %v", err)
|
||||
}
|
||||
if got1.TotalRequests != 5 || got1.SuccessRequests != 3 || got1.FailureRequests != 2 {
|
||||
t.Fatalf("key1 counts: total=%d success=%d failure=%d", got1.TotalRequests, got1.SuccessRequests, got1.FailureRequests)
|
||||
}
|
||||
if math.Abs(got1.SuccessRate-0.6) > 1e-6 || math.Abs(got1.FailureRate-0.4) > 1e-6 {
|
||||
t.Fatalf("key1 rates: success=%f failure=%f", got1.SuccessRate, got1.FailureRate)
|
||||
}
|
||||
|
||||
var got2 model.APIKey
|
||||
if err := db.First(&got2, key2.ID).Error; err != nil {
|
||||
t.Fatalf("load key2: %v", err)
|
||||
}
|
||||
if got2.TotalRequests != 4 || got2.SuccessRequests != 4 || got2.FailureRequests != 0 {
|
||||
t.Fatalf("key2 counts: total=%d success=%d failure=%d", got2.TotalRequests, got2.SuccessRequests, got2.FailureRequests)
|
||||
}
|
||||
if math.Abs(got2.SuccessRate-1.0) > 1e-6 || math.Abs(got2.FailureRate-0.0) > 1e-6 {
|
||||
t.Fatalf("key2 rates: success=%f failure=%f", got2.SuccessRate, got2.FailureRate)
|
||||
}
|
||||
|
||||
var gotGroup model.ProviderGroup
|
||||
if err := db.First(&gotGroup, group.ID).Error; err != nil {
|
||||
t.Fatalf("load group: %v", err)
|
||||
}
|
||||
if gotGroup.TotalRequests != 9 || gotGroup.SuccessRequests != 7 || gotGroup.FailureRequests != 2 {
|
||||
t.Fatalf("group counts: total=%d success=%d failure=%d", gotGroup.TotalRequests, gotGroup.SuccessRequests, gotGroup.FailureRequests)
|
||||
}
|
||||
if math.Abs(gotGroup.SuccessRate-(7.0/9.0)) > 1e-6 || math.Abs(gotGroup.FailureRate-(2.0/9.0)) > 1e-6 {
|
||||
t.Fatalf("group rates: success=%f failure=%f", gotGroup.SuccessRate, gotGroup.FailureRate)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user