updates
All checks were successful
Build and Deploy / Build & Push Image (push) Successful in 33s

This commit is contained in:
2026-03-25 20:45:57 -06:00
parent 5184b60e9c
commit 0fbb03f7de
5 changed files with 729 additions and 13 deletions

View File

@@ -0,0 +1,89 @@
defmodule CobblemonUi.PokeApi do
@moduledoc """
Fetches and caches Pokémon type data from PokeAPI.
Uses an ETS table for fast lookups. Data is fetched once per species
and cached for the lifetime of the process.
"""
use GenServer
require Logger
@table :poke_api_types_cache
# ---------------------------------------------------------------------------
# Client API
# ---------------------------------------------------------------------------
def start_link(_opts) do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
@doc """
Returns a list of type names (lowercase strings) for the given species.
Example: `get_types("bulbasaur")` → `["grass", "poison"]`
"""
@spec get_types(String.t()) :: [String.t()]
def get_types(species) when is_binary(species) do
key = String.downcase(species)
case :ets.lookup(@table, key) do
[{^key, types}] ->
types
[] ->
GenServer.call(__MODULE__, {:fetch_types, key}, 15_000)
end
end
# ---------------------------------------------------------------------------
# Server callbacks
# ---------------------------------------------------------------------------
@impl true
def init(_opts) do
:ets.new(@table, [:named_table, :set, :public, read_concurrency: true])
{:ok, %{}}
end
@impl true
def handle_call({:fetch_types, species}, _from, state) do
result =
case :ets.lookup(@table, species) do
[{^species, types}] ->
types
[] ->
types = fetch_types(species)
:ets.insert(@table, {species, types})
types
end
{:reply, result, state}
end
# ---------------------------------------------------------------------------
# Private helpers
# ---------------------------------------------------------------------------
defp fetch_types(species) do
url = "https://pokeapi.co/api/v2/pokemon/#{species}"
case Req.get(url) do
{:ok, %Req.Response{status: 200, body: %{"types" => types}}} ->
types
|> Enum.sort_by(fn t -> t["slot"] end)
|> Enum.map(fn t -> get_in(t, ["type", "name"]) end)
|> Enum.reject(&is_nil/1)
{:ok, %Req.Response{status: status}} ->
Logger.warning("[PokeApi] Type lookup failed for #{species}: HTTP #{status}")
[]
{:error, reason} ->
Logger.warning("[PokeApi] Type lookup failed for #{species}: #{inspect(reason)}")
[]
end
end
end