Phase 12: bridge completeness — expanded mapper, request replay, full shaper coverage
Mapper (src/mapper.rs):
- Restructured into ExactRoute + PrefixRoute tables (was a flat match list)
- 38 exact mappings (up from 18): SBC, Draft, FUT Champs, Division Rivals,
Card Upgrades, Notifications, Settings, Trade pile, Club update, Stats, etc.
- 7 prefix-matched routes for path-param endpoints:
/ut/game/fut/draft/{id}[/pick|/abandon] → /draft/sessions/:id[/pick|/abandon]
/ut/game/fut/champs/{id}/result|claim → /fut-champs/:id/result|claim
/ut/game/fut/store/pack/{id}/open → /packs/open/:id
/ut/game/fut/item/{id} DELETE → /collection/:id (quick-sell)
/ut/game/fut/trade/{id} DELETE → /market/listings/:id (cancel)
/ut/game/fut/sbc/set/{id}/submission → /sbc/submit
- map_to_core_with_method() public helper preserves HTTP method for prefix routes
- 19 mapper unit tests (up from 6)
Replay (src/routes/admin.rs):
- POST /_bridge/captures/:id/replay — loads a capture from disk, re-runs it
against Core with original headers+body, returns original vs new response
plus a structured JSON diff for iterative shaper/handler development
Shaper (src/shaper.rs):
- 14 new shaper functions covering every mapped Core path:
collection, cards, objectives, notifications, division, statistics,
sbc, pack-open, draft, fut-champs, chemistry-styles, my-listings
- Prefix dispatch for /packs/open/, /draft/sessions/, /fut-champs/ paths
- All 25 unit tests pass, clippy clean
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+140
-7
@@ -10,15 +10,40 @@ use uuid::Uuid;
|
||||
/// Shape a Core JSON response into the format FIFA 23 expects for this path.
|
||||
/// Returns the shaped response or the original unchanged if no shaping is defined.
|
||||
pub fn shape_response(core_path: &str, core_response: Value) -> Value {
|
||||
// Exact matches
|
||||
match core_path {
|
||||
"/auth/local" => shape_auth_response(core_response),
|
||||
"/profile" => shape_profile_response(core_response),
|
||||
"/club" => shape_club_response(core_response),
|
||||
"/squad" => shape_squad_response(core_response),
|
||||
"/market" => shape_market_response(core_response),
|
||||
"/packs" => shape_packs_response(core_response),
|
||||
_ => core_response,
|
||||
"/auth/local" => return shape_auth_response(core_response),
|
||||
"/profile" => return shape_profile_response(core_response),
|
||||
"/club" => return shape_club_response(core_response),
|
||||
"/squad" => return shape_squad_response(core_response),
|
||||
"/market" => return shape_market_response(core_response),
|
||||
"/market/my-listings" => return shape_my_listings_response(core_response),
|
||||
"/packs" => return shape_packs_response(core_response),
|
||||
"/collection" => return shape_collection_response(core_response),
|
||||
"/cards" => return shape_cards_response(core_response),
|
||||
"/objectives" => return shape_objectives_response(core_response),
|
||||
"/notifications" => return shape_notifications_response(core_response),
|
||||
"/division" => return shape_division_response(core_response),
|
||||
"/statistics" => return shape_statistics_response(core_response),
|
||||
"/sbc" => return shape_sbc_response(core_response),
|
||||
"/fut-champs" => return shape_fut_champs_response(core_response),
|
||||
"/chemistry-styles" => return shape_chemistry_styles_response(core_response),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Prefix matches for parameterised paths
|
||||
if core_path.starts_with("/packs/open/") {
|
||||
return shape_pack_open_response(core_response);
|
||||
}
|
||||
if core_path.starts_with("/draft/sessions/") {
|
||||
return shape_draft_response(core_response);
|
||||
}
|
||||
if core_path.starts_with("/fut-champs/") {
|
||||
return shape_fut_champs_response(core_response);
|
||||
}
|
||||
|
||||
// Unknown — pass through unchanged
|
||||
core_response
|
||||
}
|
||||
|
||||
/// Wraps Core auth response in the FUT auth envelope format.
|
||||
@@ -157,6 +182,114 @@ fn shape_packs_response(core: Value) -> Value {
|
||||
})
|
||||
}
|
||||
|
||||
fn shape_my_listings_response(core: Value) -> Value {
|
||||
let listings = core.get("listings").cloned().unwrap_or(json!([]));
|
||||
json!({
|
||||
"tradepile": listings,
|
||||
"credits": 0,
|
||||
"openfut": true,
|
||||
})
|
||||
}
|
||||
|
||||
fn shape_collection_response(core: Value) -> Value {
|
||||
let items = core.get("collection").cloned().unwrap_or(json!([]));
|
||||
let total = core.get("total").cloned().unwrap_or(json!(0));
|
||||
json!({
|
||||
"itemData": items,
|
||||
"total": total,
|
||||
"openfut": true,
|
||||
})
|
||||
}
|
||||
|
||||
fn shape_cards_response(core: Value) -> Value {
|
||||
let cards = core.get("cards").cloned().unwrap_or(json!([]));
|
||||
let total = core.get("total").cloned().unwrap_or(json!(0));
|
||||
json!({
|
||||
"items": cards,
|
||||
"total": total,
|
||||
"openfut": true,
|
||||
})
|
||||
}
|
||||
|
||||
fn shape_objectives_response(core: Value) -> Value {
|
||||
let objectives = core.get("objectives").cloned().unwrap_or(json!([]));
|
||||
json!({
|
||||
"objectives": objectives,
|
||||
"openfut": true,
|
||||
})
|
||||
}
|
||||
|
||||
fn shape_notifications_response(core: Value) -> Value {
|
||||
let notifications = core.get("notifications").cloned().unwrap_or(json!([]));
|
||||
let count = core.get("count").cloned().unwrap_or(json!(0));
|
||||
json!({
|
||||
"notifications": notifications,
|
||||
"count": count,
|
||||
"openfut": true,
|
||||
})
|
||||
}
|
||||
|
||||
fn shape_division_response(core: Value) -> Value {
|
||||
json!({
|
||||
"divisionRivalsInfo": {
|
||||
"division": core.get("division").cloned().unwrap_or(json!(5)),
|
||||
"seasonNumber": core.get("season_number").cloned().unwrap_or(json!(1)),
|
||||
"points": core.get("season_points").cloned().unwrap_or(json!(0)),
|
||||
"matchesPlayed": core.get("matches_played").cloned().unwrap_or(json!(0)),
|
||||
"matchesRemaining": core.get("matches_remaining").cloned().unwrap_or(json!(10)),
|
||||
"record": core.get("record").cloned().unwrap_or(json!({})),
|
||||
},
|
||||
"openfut": true,
|
||||
"_raw": core,
|
||||
})
|
||||
}
|
||||
|
||||
fn shape_statistics_response(core: Value) -> Value {
|
||||
json!({
|
||||
"stats": core,
|
||||
"openfut": true,
|
||||
})
|
||||
}
|
||||
|
||||
fn shape_sbc_response(core: Value) -> Value {
|
||||
let sbcs = core.get("sbcs").cloned().unwrap_or(json!([]));
|
||||
json!({
|
||||
"challenges": sbcs,
|
||||
"openfut": true,
|
||||
})
|
||||
}
|
||||
|
||||
fn shape_pack_open_response(core: Value) -> Value {
|
||||
let cards = core.get("cards").cloned().unwrap_or(json!([]));
|
||||
json!({
|
||||
"itemData": cards,
|
||||
"duplicateItemIdList": [],
|
||||
"openfut": true,
|
||||
})
|
||||
}
|
||||
|
||||
fn shape_draft_response(core: Value) -> Value {
|
||||
json!({
|
||||
"draft": core,
|
||||
"openfut": true,
|
||||
})
|
||||
}
|
||||
|
||||
fn shape_fut_champs_response(core: Value) -> Value {
|
||||
json!({
|
||||
"futChampions": core,
|
||||
"openfut": true,
|
||||
})
|
||||
}
|
||||
|
||||
fn shape_chemistry_styles_response(core: Value) -> Value {
|
||||
let styles = core.get("chemistry_styles").cloned().unwrap_or(json!([]));
|
||||
json!({
|
||||
"chemistryStyles": styles,
|
||||
"openfut": true,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
Reference in New Issue
Block a user