feat: map objectives, SBC, squad battles, and match endpoints
Adds exact and prefix routes for endpoints observed in FIFA 23 FUT that
were not yet covered:
- GET /ut/game/fut/user/dynamicobjectives → GET /objectives
- POST /ut/game/fut/user/dynamicobjectives/{id}/milestones/{m}/claim
→ POST /objectives/{id}/claim
- GET /ut/game/fut/sbs/challenges[/{id}] → GET /sbc[/{id}]
- POST /ut/game/fut/sbs/challenges/{id}/sets/{setId}/trade → POST /sbc/submit
- POST /ut/game/fut/squadbattle/result → POST /matches/result
- POST /ut/game/fut/game/r → POST /matches/result
- GET /ut/game/fut/squads/user → GET /squad
Also fixes prefix-route method forwarding bug: prefix routes were
unconditionally returning method "GET" instead of the matched EA method.
Closes #3 #4
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+88
-3
@@ -381,6 +381,36 @@ const EXACT: &[ExactRoute] = &[
|
|||||||
core_method: "GET", core_path: "/notifications",
|
core_method: "GET", core_path: "/notifications",
|
||||||
notes: "FUT active messages → Core notifications (mapped to nearest equivalent)",
|
notes: "FUT active messages → Core notifications (mapped to nearest equivalent)",
|
||||||
},
|
},
|
||||||
|
// ── Dynamic objectives (FIFA 23 actual path) ──────────────────────────────
|
||||||
|
ExactRoute {
|
||||||
|
ea_method: "GET", ea_path: "/ut/game/fut/user/dynamicobjectives",
|
||||||
|
core_method: "GET", core_path: "/objectives",
|
||||||
|
notes: "FIFA 23 dynamic objectives list → Core objectives",
|
||||||
|
},
|
||||||
|
// ── SBS challenges (FIFA 23 uses /sbs/ not /sbc/) ────────────────────────
|
||||||
|
ExactRoute {
|
||||||
|
ea_method: "GET", ea_path: "/ut/game/fut/sbs/challenges",
|
||||||
|
core_method: "GET", core_path: "/sbc",
|
||||||
|
notes: "FIFA 23 SBS challenges list → Core SBC list",
|
||||||
|
},
|
||||||
|
// ── Squad Battles result (separate from rivals/result) ───────────────────
|
||||||
|
ExactRoute {
|
||||||
|
ea_method: "POST", ea_path: "/ut/game/fut/squadbattle/result",
|
||||||
|
core_method: "POST", core_path: "/matches/result",
|
||||||
|
notes: "Squad Battles match result → Core match result",
|
||||||
|
},
|
||||||
|
// ── Match submission (FIFA 23 uses /game/r) ───────────────────────────────
|
||||||
|
ExactRoute {
|
||||||
|
ea_method: "POST", ea_path: "/ut/game/fut/game/r",
|
||||||
|
core_method: "POST", core_path: "/matches/result",
|
||||||
|
notes: "FIFA 23 match result (short path /game/r) → Core match result",
|
||||||
|
},
|
||||||
|
// ── Squad list ────────────────────────────────────────────────────────────
|
||||||
|
ExactRoute {
|
||||||
|
ea_method: "GET", ea_path: "/ut/game/fut/squads/user",
|
||||||
|
core_method: "GET", core_path: "/squad",
|
||||||
|
notes: "FUT squad list for user → Core squad",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
// ── Prefix mappings (dynamic path segments after a known prefix) ───────────────
|
// ── Prefix mappings (dynamic path segments after a known prefix) ───────────────
|
||||||
@@ -431,6 +461,36 @@ fn prefix_routes() -> &'static [PrefixRoute] {
|
|||||||
core_path_fn: |_| "/sbc/submit".to_string(),
|
core_path_fn: |_| "/sbc/submit".to_string(),
|
||||||
notes: "FUT SBC set submission → Core SBC submit",
|
notes: "FUT SBC set submission → Core SBC submit",
|
||||||
},
|
},
|
||||||
|
// /ut/game/fut/sbs/challenges/{id} → GET /sbc/{id}
|
||||||
|
PrefixRoute {
|
||||||
|
ea_method: "GET",
|
||||||
|
ea_prefix: "/ut/game/fut/sbs/challenges/",
|
||||||
|
core_path_fn: |suffix| {
|
||||||
|
// suffix is "{id}" or "{id}/sets/{setId}" — strip extra segments
|
||||||
|
let id = suffix.split('/').next().unwrap_or(suffix);
|
||||||
|
format!("/sbc/{id}")
|
||||||
|
},
|
||||||
|
notes: "FIFA 23 SBS challenge detail → Core SBC by ID",
|
||||||
|
},
|
||||||
|
// /ut/game/fut/sbs/challenges/{id}/sets/{setId}/trade → POST /sbc/submit
|
||||||
|
PrefixRoute {
|
||||||
|
ea_method: "POST",
|
||||||
|
ea_prefix: "/ut/game/fut/sbs/challenges/",
|
||||||
|
core_path_fn: |_| "/sbc/submit".to_string(),
|
||||||
|
notes: "FIFA 23 SBS challenge trade → Core SBC submit",
|
||||||
|
},
|
||||||
|
// /ut/game/fut/user/dynamicobjectives/{id}/milestones/{m}/claim
|
||||||
|
// → POST /objectives/{id}/claim
|
||||||
|
PrefixRoute {
|
||||||
|
ea_method: "POST",
|
||||||
|
ea_prefix: "/ut/game/fut/user/dynamicobjectives/",
|
||||||
|
core_path_fn: |suffix| {
|
||||||
|
// suffix is "{id}/milestones/{m}/claim" — extract just the obj id
|
||||||
|
let obj_id = suffix.split('/').next().unwrap_or(suffix);
|
||||||
|
format!("/objectives/{obj_id}/claim")
|
||||||
|
},
|
||||||
|
notes: "FIFA 23 dynamic objective claim → Core objective claim by ID",
|
||||||
|
},
|
||||||
// /ut/game/fut/trade/{trade_id} (DELETE) → DELETE /market/listings/{trade_id}
|
// /ut/game/fut/trade/{trade_id} (DELETE) → DELETE /market/listings/{trade_id}
|
||||||
PrefixRoute {
|
PrefixRoute {
|
||||||
ea_method: "DELETE",
|
ea_method: "DELETE",
|
||||||
@@ -523,7 +583,7 @@ pub fn map_to_core(method: &str, path: &str) -> Option<CoreMapping> {
|
|||||||
if !suffix.is_empty() {
|
if !suffix.is_empty() {
|
||||||
let core_path = (r.core_path_fn)(suffix);
|
let core_path = (r.core_path_fn)(suffix);
|
||||||
return Some(CoreMapping {
|
return Some(CoreMapping {
|
||||||
method: "GET", // overridden per-route below
|
method: r.ea_method,
|
||||||
core_path,
|
core_path,
|
||||||
notes: r.notes,
|
notes: r.notes,
|
||||||
});
|
});
|
||||||
@@ -720,14 +780,39 @@ mod tests {
|
|||||||
fn test_quick_sell_maps() {
|
fn test_quick_sell_maps() {
|
||||||
let m = map_to_core("DELETE", "/ut/game/fut/item/owned-111");
|
let m = map_to_core("DELETE", "/ut/game/fut/item/owned-111");
|
||||||
assert!(m.is_some());
|
assert!(m.is_some());
|
||||||
assert_eq!(m.unwrap().core_path, "/collection/owned-111");
|
let m = m.unwrap();
|
||||||
|
assert_eq!(m.core_path, "/collection/owned-111");
|
||||||
|
assert_eq!(m.method, "DELETE");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_cancel_listing_maps() {
|
fn test_cancel_listing_maps() {
|
||||||
let m = map_to_core("DELETE", "/ut/game/fut/trade/trade-222");
|
let m = map_to_core("DELETE", "/ut/game/fut/trade/trade-222");
|
||||||
assert!(m.is_some());
|
assert!(m.is_some());
|
||||||
assert_eq!(m.unwrap().core_path, "/market/listings/trade-222");
|
let m = m.unwrap();
|
||||||
|
assert_eq!(m.core_path, "/market/listings/trade-222");
|
||||||
|
assert_eq!(m.method, "DELETE");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_prefix_routes_preserve_method() {
|
||||||
|
let cases = [
|
||||||
|
("POST", "/ut/game/fut/draft/abc-123/pick", "POST"),
|
||||||
|
("POST", "/ut/game/fut/champs/sess-456/result", "POST"),
|
||||||
|
("DELETE", "/ut/game/fut/trade/trade-222", "DELETE"),
|
||||||
|
("DELETE", "/ut/game/fut/item/owned-111", "DELETE"),
|
||||||
|
("PUT", "/ut/game/fut/item/owned-999", "PUT"),
|
||||||
|
("POST", "/ut/game/fut/store/pack/pack-789/open", "POST"),
|
||||||
|
];
|
||||||
|
for (ea_method, path, expected_method) in cases {
|
||||||
|
let m = map_to_core(ea_method, path)
|
||||||
|
.unwrap_or_else(|| panic!("no mapping for {ea_method} {path}"));
|
||||||
|
assert_eq!(
|
||||||
|
m.method, expected_method,
|
||||||
|
"{ea_method} {path} → expected method {expected_method}, got {}",
|
||||||
|
m.method
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user