feat: serve fort map data and filter pick lists from Golbat's fort API - #175
feat: serve fort map data and filter pick lists from Golbat's fort API#175jfberry wants to merge 17 commits into
Conversation
Plan for serving gym/pokestop/station map data from Golbat's fort API (UnownHash/Golbat#385) with automatic detection and SQL fallback, plus filter pick lists from fort availability. Agreed design: ccev#174. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The SQL "boss" OR-branch (COALESCE(raid_pokemon_id, 0) != 0) is unconditional and doesn't fold in `levels`. buildGymDnfFilters was narrowing it to filterset.levels when present, which could exclude a hatched raid at a level outside that filterset's `levels` list - silently hiding it from the map. Push an any-active-raid clause instead, matching the egg branch's unconditional style; the local filter pass re-checks the hatched/level constraint.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…lure AMOUNT_MAX capped quest reward amount ranges at 10000, silently dropping quests above that threshold since the SQL fallback omitted the max clause entirely. Raise it to int32 max so the DNF range can never exclude a real amount. Also add per-request SQL fallback to ApiGymQuery/ApiPokestopQuery/ ApiStationQuery: a thrown fetch error (e.g. Golbat dying mid-request) or an undefined scan/by-id result now falls back to the parent SQL query instead of always 500ing or silently missing data. Found-but-deleted objects still short-circuit to [] as before.
Golbat's fort API sends quest_rewards / alternative_quest_rewards as real JSON arrays, while parseQuestReward (via the inherited prepare) expects the SQL rows' serialized string form — every quest-bearing pokestop threw and the route 500'd. Re-serialize both fields in the mapper, correct the wire type that hid the mismatch, and move the mapper to its own type-only-import module so it is unit-testable under bare vitest. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The fort API's design is native JSON for blob columns, but the pass was incomplete: gyms were fully converted while stationed_pokemon, showcase_focus, showcase_rankings and both quest_conditions still arrived as escaped strings. Convert all five via jsonRaw, matching the gym rsvps precedent. Consumers are unmerged (ccev/diadem#175, WatWowMap/ReactMap#1228 — the latter already handles both forms), so now is the time to break the wire. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Golbat is finishing its fort-API native-JSON conversion (UnownHash/Golbat#393): stationed_pokemon, showcase_focus and showcase_rankings switch from serialized strings to real JSON. Accept both wire generations via blobToString so either Golbat works, and type the wire fields honestly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Heads-up on a cross-PR dependency: UnownHash/Golbat#393 now finishes the fort API's native-JSON conversion — |
|
Proposal for a follow-up PR (deliberately not part of this one) — flipping the internal fort-data contract to parsed JSON: Right now the internal contract after a query is "the SQL row's shape", i.e. JSON blobs as serialized strings, and The follow-up would normalize at each source boundary instead:
Benefits: one canonical internal shape, the wire-vs-DB trap class is gone, no stringify→parse round-trip per scan. Cost: touches One counterweight to weigh: if the fort SQL path eventually retires once Any objections to this direction? If you're happy I'd do it as a small standalone PR once this one lands. |
Implements the design agreed in #174: gyms, pokéstops and stations are served from Golbat's fort HTTP API (UnownHash/Golbat#385) instead of raw SQL, and filter pick lists are sourced from
GET api/fort/available— with automatic detection and full SQL fallback. Draft until the live parity checks below are done.What's here (Phase 1 + Phase 2 of #174)
golbatFortApi.ts): a 60s poll ofapi/fort/availabledoubles as feature detection (the endpoints are 503-gated onfort_in_memory). No config option —golbat.url/golbat.secretare all that's needed. Golbat can be toggled/upgraded without restarting Diadem. Unreachable-Golbat network errors are tolerated at startup and at runtime; the detection probe is bounded by a 10s timeout.ApiGymQuery/ApiPokestopQuery/ApiStationQuery) subclassing the SQL classes and overriding onlyquery()/querySingle()— allfilter()/prepare()logic is inherited, same as thePokemonQuerypattern. If a scan or by-id call fails mid-request (e.g. Golbat dies inside the 60s detection window), the request falls back to the parent SQL implementation instead of 500ing.fortDnf.ts, unit-tested): each SQL OR-branch becomes one DNF clause; everything DNF can't express (bosstemp_evolution_id, quest title/target,ranking_standard, exact item amounts,bread_mode) is loosened to a superset and re-checked by the existing local filter logic.MasterStatsat/api/statsrequest time — zero component churn): raid bosses, quest rewards (incl. counts), max battles and showcases now refresh within ~60s instead of hourly. The three live-table SQL queries inqueryMasterStats()(incl. the questUNION ALL— the most expensive recurring query) are skipped while the fort API is on. On an API→SQL fallback transition the stats provider refreshes immediately so pick lists don't sit empty until the hourly tick.Accepted behavioral deltas (from #174 / Appendix B)
count(nothing rendered them; the availability index can't provide them by design).ranking_standardis 0 in pick lists until the Golbat availability-enrichment PR lands.bread_modeon max-battle pick entries is derived frombattle_level >= 6(to be confirmed against live data, see checklist).enabledis now actually populated on gyms/pokestops (was declared but never selected by the SQL).examinedcounts, which can run higher than SQL's matched-row counts (same as the pokemon scan path).Pending live verification (why this is a draft)
Needs a Golbat with
fort_in_memory = true(preload = truerecommended):{pokemon_id: 0}must match null-boss raids like SQL'sCOALESCE(raid_pokemon_id, 0) = 0quest_rewards/showcase_focus/showcase_rankings/stationed_pokemonarrive as JSON strings (inheritedprepare()parses strings)SELECT DISTINCT battle_level, battle_pokemon_bread_mode FROM station WHERE battle_pokemon_bread_mode IS NOT NULL;— confirm gmax ⇔ level 6Upstream Golbat follow-ups (filed separately, none block this PR)
limit_reachedon fort scan responses (mirror of feat: expose v3 pokemon scan limit status UnownHash/Golbat#392 — the interimlimit + 1overflow check covers until then)temp_evolution_idon raids,ranking_standardon showcasesApiPokestopResult.FirstSeenTimestampisint16— unix timestamps truncateNotes for review
availble_slotsis intentionally preserved (DB column name); the API'savailable_slotsis mapped onto it.main) svelte-check errors: 9 errors in 5 unrelated UI files; this branch adds none.pokestop.d.tshasQuestRewardTempEvoBranch(and three sibling types) misplaced in theContestFocusunion plus a danglingQuestRewardPokemonEggreference — masked byskipLibCheck. Worth a separate issue; the one place it bites here is documented at the cast site inqueryStats.ts.🤖 Generated with Claude Code