diff --git a/esi/cached.go b/esi/cached.go index ed83c24..5d5399d 100644 --- a/esi/cached.go +++ b/esi/cached.go @@ -15,23 +15,20 @@ import ( logger "git.site.quack-lab.dev/dave/cylogger" ) -// CacheEntry is now defined in the types package +type CacheDB interface { + GetCacheEntry(urlHash string) (*types.CacheEntry, error) + SaveCacheEntry(entry *types.CacheEntry) error +} // CachedESI implements ESIInterface with caching type CachedESI struct { direct ESIInterface - db interface { - GetCacheEntry(urlHash string) (*types.CacheEntry, error) - SaveCacheEntry(entry *types.CacheEntry) error - } + db CacheDB cacheValidity time.Duration } // NewCachedESI creates a new CachedESI instance -func NewCachedESI(direct ESIInterface, db interface { - GetCacheEntry(urlHash string) (*types.CacheEntry, error) - SaveCacheEntry(entry *types.CacheEntry) error -}) *CachedESI { +func NewCachedESI(direct ESIInterface, db CacheDB) *CachedESI { // Parse cache validity ONCE at initialization cacheValidity, err := time.ParseDuration(options.GlobalOptions.CacheValidity) if err != nil { @@ -47,113 +44,90 @@ func NewCachedESI(direct ESIInterface, db interface { } } -// 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) - logger.Info("CachedESI.GetCharacterPlanets: Starting for character %d", characterID) +// GetPlanetPI retrieves detailed information about a specific planet with caching +func (c *CachedESI) GetPlanetPI(ctx context.Context, characterID int, planetID int64, accessToken string) (*PlanetPI, error) { + funclog := logger.Default.WithPrefix("CachedESI.GetPlanetPI").WithPrefix(fmt.Sprintf("characterID=%d", characterID)).WithPrefix(fmt.Sprintf("planetID=%d", planetID)) - fetchFunc := func() ([]Planet, error) { - logger.Info("CachedESI.GetCharacterPlanets: Calling direct ESI for character %d planets", characterID) - result, err := c.direct.GetCharacterPlanets(ctx, characterID, accessToken) - if err != nil { - logger.Error("CachedESI.GetCharacterPlanets: Direct ESI call failed for character %d: %v", characterID, err) - return nil, err - } - logger.Info("CachedESI.GetCharacterPlanets: Direct ESI returned %d planets for character %d", len(result), characterID) - return result, nil - } - - result, err := getCachedResponse(c, url, fetchFunc) - if err != nil { - logger.Error("CachedESI.GetCharacterPlanets: Failed to get planets for character %d: %v", characterID, err) - return nil, err - } - - logger.Info("CachedESI.GetCharacterPlanets: Successfully retrieved %d planets for character %d", len(result), characterID) - return result, 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) - logger.Info("CachedESI.GetPlanetDetails: Starting for character %d, planet %d", characterID, planetID) + funclog.Info("Starting") - fetchFunc := func() (*PlanetDetail, error) { - logger.Info("CachedESI.GetPlanetDetails: Calling direct ESI for character %d planet %d details", characterID, planetID) - result, err := c.direct.GetPlanetDetails(ctx, characterID, planetID, accessToken) + fetchFunc := func() (*PlanetPI, error) { + funclog.Info("Calling direct ESI") + result, err := c.direct.GetPlanetPI(ctx, characterID, planetID, accessToken) if err != nil { - logger.Error("CachedESI.GetPlanetDetails: Direct ESI call failed for character %d planet %d: %v", characterID, planetID, err) + funclog.Error("Direct ESI call failed: %v", err) return nil, err } if result != nil { - logger.Info("CachedESI.GetPlanetDetails: Direct ESI returned planet details with %d pins for character %d planet %d", len(result.Pins), characterID, planetID) + funclog.Info("Direct ESI returned planet details with %d pins", len(result.Pins)) } else { - logger.Info("CachedESI.GetPlanetDetails: Direct ESI returned nil planet details for character %d planet %d", characterID, planetID) + funclog.Info("Direct ESI returned nil planet details") } return result, nil } result, err := getCachedResponse(c, url, fetchFunc) if err != nil { - logger.Error("CachedESI.GetPlanetDetails: Failed to get planet details for character %d, planet %d: %v", characterID, planetID, err) + funclog.Error("Failed to get planet details: %v", err) return nil, err } if result != nil { - logger.Info("CachedESI.GetPlanetDetails: Successfully retrieved planet details with %d pins for character %d, planet %d", len(result.Pins), characterID, planetID) + funclog.Info("Successfully retrieved planet details with %d pins", len(result.Pins)) } else { - logger.Info("CachedESI.GetPlanetDetails: Successfully retrieved nil planet details for character %d, planet %d", characterID, planetID) + funclog.Info("Successfully retrieved nil planet details") } return result, nil } // getCachedResponse handles caching logic with generics func getCachedResponse[T any](c *CachedESI, url string, fetchFunc func() (T, error)) (T, error) { + funclog := logger.Default.WithPrefix("getCachedResponse").WithPrefix(fmt.Sprintf("url=%s", url)) // Generate cache key hash := sha256.Sum256([]byte(url)) urlHash := hex.EncodeToString(hash[:]) - logger.Debug("CachedESI: Generated cache key %s for URL: %s", urlHash[:8], url) + funclog.Debug("Generated cache key %s for URL: %s", urlHash[:8], url) // Check cache using the interface - logger.Debug("CachedESI: Checking cache for URL: %s", url) + funclog.Debug("Checking cache for URL: %s", url) cacheEntry, err := c.db.GetCacheEntry(urlHash) if err == nil { // Check if cache is still valid age := time.Since(cacheEntry.CachedAt) if age < c.cacheValidity { - logger.Info("Cache HIT for URL: %s (cached %v ago, valid for %v more)", url, age, c.cacheValidity-age) + funclog.Info("Cache HIT for URL: %s (cached %v ago, valid for %v more)", url, age, c.cacheValidity-age) // Parse cached response var result T if err := json.Unmarshal([]byte(cacheEntry.Response), &result); err == nil { - logger.Debug("CachedESI: Successfully unmarshaled cached response for URL: %s", url) + funclog.Debug("Successfully unmarshaled cached response for URL: %s", url) return result, nil } - logger.Warning("Failed to unmarshal cached response for URL: %s: %v", url, err) + funclog.Warning("Failed to unmarshal cached response for URL: %s: %v", url, err) } else { logger.Info("Cache EXPIRED for URL: %s (cached %v ago, validity: %v)", url, age, c.cacheValidity) } } else { - logger.Info("Cache MISS for URL: %s (no cache entry found): %v", url, err) + funclog.Info("Cache MISS for URL: %s (no cache entry found): %v", url, err) } // Cache miss or invalid, fetch from API - logger.Info("Fetching from API for URL: %s", url) + funclog.Info("Fetching from API for URL: %s", url) result, err := fetchFunc() if err != nil { - logger.Error("CachedESI: API fetch failed for URL: %s: %v", url, err) + funclog.Error("API fetch failed for URL: %s: %v", url, err) var zero T return zero, err } - logger.Debug("CachedESI: API fetch successful for URL: %s", url) + funclog.Debug("API fetch successful for URL: %s", url) // Store in cache - logger.Debug("CachedESI: Marshaling response for caching URL: %s", url) + funclog.Debug("Marshaling response for caching URL: %s", url) responseBytes, err := json.Marshal(result) if err != nil { - logger.Warning("Failed to marshal response for caching URL: %s: %v", url, err) + funclog.Warning("Failed to marshal response for caching URL: %s: %v", url, err) return result, nil } - logger.Debug("CachedESI: Response marshaled successfully for URL: %s (%d bytes)", url, len(responseBytes)) + funclog.Debug("Response marshaled successfully for URL: %s (%d bytes)", url, len(responseBytes)) cacheEntry = &types.CacheEntry{ URLHash: urlHash, @@ -161,46 +135,47 @@ func getCachedResponse[T any](c *CachedESI, url string, fetchFunc func() (T, err CachedAt: time.Now(), } - logger.Debug("CachedESI: Saving cache entry for URL: %s", url) + funclog.Debug("Saving cache entry for URL: %s", url) if err := c.db.SaveCacheEntry(cacheEntry); err != nil { - logger.Warning("Failed to cache response for URL: %s: %v", url, err) + funclog.Warning("Failed to cache response for URL: %s: %v", url, err) } else { - logger.Info("Cached response for URL: %s (valid for %v, %d bytes)", url, c.cacheValidity, len(responseBytes)) + funclog.Info("Cached response for URL: %s (valid for %v, %d bytes)", url, c.cacheValidity, len(responseBytes)) } return result, nil } // GetPlanetName retrieves planet name with caching -func (c *CachedESI) GetPlanetName(ctx context.Context, planetID int) (*PlanetName, error) { +func (c *CachedESI) GetPlanetName(ctx context.Context, planetID int64) (*PlanetName, error) { + funclog := logger.Default.WithPrefix("CachedESI.GetPlanetName").WithPrefix(fmt.Sprintf("planetID=%d", planetID)) url := fmt.Sprintf("/v1/universe/planets/%d/", planetID) - logger.Info("CachedESI.GetPlanetName: Starting for planet %d", planetID) + funclog.Info("Starting") fetchFunc := func() (*PlanetName, error) { - logger.Info("CachedESI.GetPlanetName: Calling direct ESI for planet %d name", planetID) + funclog.Info("Calling direct ESI for planet %d name", planetID) result, err := c.direct.GetPlanetName(ctx, planetID) if err != nil { - logger.Error("CachedESI.GetPlanetName: Direct ESI call failed for planet %d: %v", planetID, err) + funclog.Error("Direct ESI call failed for planet %d: %v", planetID, err) return nil, err } if result != nil { - logger.Info("CachedESI.GetPlanetName: Direct ESI returned planet name '%s' for planet %d", result.Name, planetID) + funclog.Info("Direct ESI returned planet name '%s' for planet %d", result.Name, planetID) } else { - logger.Info("CachedESI.GetPlanetName: Direct ESI returned nil planet name for planet %d", planetID) + funclog.Info("Direct ESI returned nil planet name for planet %d", planetID) } return result, nil } result, err := getCachedResponse(c, url, fetchFunc) if err != nil { - logger.Error("CachedESI.GetPlanetName: Failed to get planet name for planet %d: %v", planetID, err) + funclog.Error("Failed to get planet name for planet %d: %v", planetID, err) return nil, err } if result != nil { - logger.Info("CachedESI.GetPlanetName: Successfully retrieved planet name '%s' for planet %d", result.Name, planetID) + funclog.Info("Successfully retrieved planet name '%s' for planet %d", result.Name, planetID) } else { - logger.Info("CachedESI.GetPlanetName: Successfully retrieved nil planet name for planet %d", planetID) + funclog.Info("Successfully retrieved nil planet name for planet %d", planetID) } return result, nil } diff --git a/esi/types.go b/esi/types.go new file mode 100644 index 0000000..99dba82 --- /dev/null +++ b/esi/types.go @@ -0,0 +1,51 @@ +package esi + +import "time" + +type PlanetPI struct { + Links []Link `json:"links"` + Pins []Pin `json:"pins"` + Routes []Route `json:"routes"` +} + +type Link struct { + DestinationPinID int64 `json:"destination_pin_id"` + LinkLevel int64 `json:"link_level"` + SourcePinID int64 `json:"source_pin_id"` +} + +type Pin struct { + Contents []Content `json:"contents"` + LastCycleStart *time.Time `json:"last_cycle_start,omitempty"` + Latitude float64 `json:"latitude"` + Longitude float64 `json:"longitude"` + PinID int64 `json:"pin_id"` + TypeID int64 `json:"type_id"` + SchematicID *int64 `json:"schematic_id,omitempty"` +} + +type Content struct { + Amount int64 `json:"amount"` + TypeID int64 `json:"type_id"` +} + +type Route struct { + ContentTypeID int64 `json:"content_type_id"` + DestinationPinID int64 `json:"destination_pin_id"` + Quantity int64 `json:"quantity"` + RouteID int64 `json:"route_id"` + SourcePinID int64 `json:"source_pin_id"` + Waypoints []int64 `json:"waypoints"` +} + +type Planets []Planet + +type Planet struct { + LastUpdate time.Time `json:"last_update"` + NumPins int64 `json:"num_pins"` + OwnerID int64 `json:"owner_id"` + PlanetID int64 `json:"planet_id"` + PlanetType string `json:"planet_type"` + SolarSystemID int64 `json:"solar_system_id"` + UpgradeLevel int64 `json:"upgrade_level"` +}