Hallucinate a direct and cached esi apis

This commit is contained in:
2025-10-10 22:16:42 +02:00
parent d0075c35cc
commit 5433247e2b
2 changed files with 150 additions and 2 deletions

128
esi/cached.go Normal file
View File

@@ -0,0 +1,128 @@
package esi
import (
"context"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"time"
logger "git.site.quack-lab.dev/dave/cylogger"
)
// CacheEntry represents a cached API response
type CacheEntry struct {
ID uint `gorm:"primaryKey"`
URLHash string `gorm:"uniqueIndex"`
Response string `gorm:"type:text"`
CachedAt time.Time `gorm:"index"`
}
// CachedESI implements ESIInterface with caching
type CachedESI struct {
direct ESIInterface
db interface {
GetCacheEntry(urlHash string) (*CacheEntry, error)
SaveCacheEntry(entry *CacheEntry) error
}
}
// NewCachedESI creates a new CachedESI instance
func NewCachedESI(direct ESIInterface, db interface {
GetCacheEntry(urlHash string) (*CacheEntry, error)
SaveCacheEntry(entry *CacheEntry) error
}) *CachedESI {
return &CachedESI{
direct: direct,
db: db,
}
}
// GetCharacterPlanets retrieves a list of planets for a character with caching
func (c *CachedESI) GetCharacterPlanets(ctx context.Context, characterID int, accessToken string) ([]Planet, error) {
url := fmt.Sprintf("/v1/characters/%d/planets/", characterID)
result, err := func() (interface{}, error) {
var fetchFunc func() (interface{}, error) = func() (interface{}, error) {
return c.direct.GetCharacterPlanets(ctx, characterID, accessToken)
}
return c.getCachedResponse(url, fetchFunc)
}()
if err != nil {
return nil, err
}
return result.([]Planet), nil
}
// GetPlanetDetails retrieves detailed information about a specific planet with caching
func (c *CachedESI) GetPlanetDetails(ctx context.Context, characterID, planetID int, accessToken string) (*PlanetDetail, error) {
url := fmt.Sprintf("/v3/characters/%d/planets/%d/", characterID, planetID)
result, err := func() (interface{}, error) {
var fetchFunc func() (interface{}, error) = func() (interface{}, error) {
return c.direct.GetPlanetDetails(ctx, characterID, planetID, accessToken)
}
return c.getCachedResponse(url, fetchFunc)
}()
if err != nil {
return nil, err
}
return result.(*PlanetDetail), nil
}
// getCachedResponse handles caching logic
func (c *CachedESI) getCachedResponse(url string, fetchFunc func() (interface{}, error)) (interface{}, error) {
// Generate cache key
hash := sha256.Sum256([]byte(url))
urlHash := hex.EncodeToString(hash[:])
// Check cache using the interface
cacheEntry, err := c.db.GetCacheEntry(urlHash)
if err == nil {
// Check if cache is still valid
cacheValidity, _ := time.ParseDuration("10m") // Default 10 minutes
if time.Since(cacheEntry.CachedAt) < cacheValidity {
logger.Debug("Cache hit for URL: %s", url)
// Parse cached response based on URL pattern
if url[len(url)-1:] == "/" {
// Planets endpoint
var planets []Planet
if err := json.Unmarshal([]byte(cacheEntry.Response), &planets); err == nil {
return planets, nil
}
} else {
// Planet details endpoint
var planetDetail PlanetDetail
if err := json.Unmarshal([]byte(cacheEntry.Response), &planetDetail); err == nil {
return &planetDetail, nil
}
}
}
}
// Cache miss or invalid, fetch from API
logger.Debug("Cache miss for URL: %s", url)
result, err := fetchFunc()
if err != nil {
return nil, err
}
// Store in cache
responseBytes, err := json.Marshal(result)
if err != nil {
logger.Warning("Failed to marshal response for caching: %v", err)
return result, nil
}
cacheEntry = CacheEntry{
URLHash: urlHash,
Response: string(responseBytes),
CachedAt: time.Now(),
}
if err := c.db.SaveCacheEntry(&cacheEntry); err != nil {
logger.Warning("Failed to cache response: %v", err)
}
logger.Debug("Cached response for URL: %s", url)
return result, nil
}