feat(fifa17): ownership-backed squad manager, not opaque round-trip
Move the squad manager from an opaque, unvalidated wire ref in the squad
extension to a resolved, ownership-backed assignment (Core migration 0023
squad_managers).
- squad::to_proposed reverse-resolves the manager wire ref to a Core
owned_card_id on ProposedSquad; an unresolvable manager is reported as
an unresolved wire id (a live save is refused rather than assigning a
manager the club does not own).
- squad_ext: drop the opaque manager field from Fifa17SquadExtensionV1
(clean cutover) and the now-unused SquadEntityRef->WireItemRef From.
- squad_projection: project the manager as the STATIC_REVERSED [{id,dream}]
wire ref resolved from the owned assignment; absent -> [] (never faked).
Richer manager itemData (contract/league/nation) is INFERRED-only and
left out pending wire reversal.
- import(apply): drop a historical dangling manager ref rather than
failing the whole import (live PUTs still refuse an unresolved manager).
This commit is contained in:
@@ -77,6 +77,11 @@ pub struct SquadProjectionInput<'a> {
|
||||
/// Every owned item a slot references, keyed by `owned_card_id`. Assembled by
|
||||
/// the host in one batch — the projector only reads from it.
|
||||
pub owned: &'a HashMap<String, CoreOwnedItem>,
|
||||
/// The owned instance assigned as this squad's **manager** (Core's
|
||||
/// ownership-backed `squad_managers` assignment, migration 0023), or `None`.
|
||||
/// Projected as the FIFA `manager` wire ref resolved from ownership — never a
|
||||
/// dangling wire id, and never fabricated when absent.
|
||||
pub manager: Option<CoreOwnedItem>,
|
||||
}
|
||||
|
||||
/// Result of a projection, with the extension-freshness verdict surfaced.
|
||||
@@ -171,6 +176,17 @@ pub fn project_squad<I: ItemIdentityResolver + ?Sized>(
|
||||
}
|
||||
}
|
||||
|
||||
// Manager: the ownership-backed assignment, resolved to its FIFA wire ref.
|
||||
// Emitted as the STATIC_REVERSED `[{id, dream}]` shape (the same shape the
|
||||
// client sends on save). An owned manager with no resolvable FIFA identity
|
||||
// is omitted (non-fatal, like /club dropping an unrenderable card) rather
|
||||
// than emitted with a fabricated id. NOTE: a richer manager itemData
|
||||
// (contract/leagueId/nation) is INFERRED-only from the offline-season
|
||||
// diagnosis and deliberately NOT invented here pending wire reversal.
|
||||
let manager = match input.manager.as_ref().and_then(|m| ident.resolve(m)) {
|
||||
Some(id) => json!([{ "id": id.item_id, "dream": false }]),
|
||||
None => json!([]),
|
||||
};
|
||||
let squad = json!({
|
||||
"id": input.fifa_squad_id,
|
||||
"squadName": input.name,
|
||||
@@ -180,7 +196,7 @@ pub fn project_squad<I: ItemIdentityResolver + ?Sized>(
|
||||
"starRating": ext.client_reported.star_rating,
|
||||
"rating": ext.client_reported.rating,
|
||||
"captain": captain_wire,
|
||||
"manager": ext.manager,
|
||||
"manager": manager,
|
||||
"custom": ext.custom,
|
||||
"players": players,
|
||||
"kicktakers": ext.kicktakers,
|
||||
@@ -262,6 +278,7 @@ mod tests {
|
||||
}],
|
||||
ext,
|
||||
owned,
|
||||
manager: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,7 +289,6 @@ mod tests {
|
||||
custom: Some("[1,2,3]".into()),
|
||||
squad_type: Some("REGULAR_SQUAD".into()),
|
||||
kit_numbers: kit,
|
||||
manager: vec![],
|
||||
kicktakers: vec![],
|
||||
client_reported: Default::default(),
|
||||
}
|
||||
@@ -403,6 +419,7 @@ mod tests {
|
||||
],
|
||||
ext: SquadExtInput::Fresh(ext),
|
||||
owned: owned_ref,
|
||||
manager: None,
|
||||
};
|
||||
let SquadProjection::Projected(v) = project_squad(&input, &ident, &ent()).unwrap() else {
|
||||
panic!();
|
||||
@@ -423,4 +440,65 @@ mod tests {
|
||||
"kit stays with the instance"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manager_projected_from_ownership_as_wire_ref() {
|
||||
let mut owned = HashMap::new();
|
||||
owned.insert("oc1".to_string(), owned_item("oc1", "card_x"));
|
||||
let ident = TableIdentity(HashMap::from([
|
||||
(
|
||||
"oc1".to_string(),
|
||||
Fifa17Identity {
|
||||
item_id: 100000042,
|
||||
asset_id: 20801,
|
||||
resource_id: 20801,
|
||||
rareflag: 1,
|
||||
},
|
||||
),
|
||||
(
|
||||
"oc-mgr".to_string(),
|
||||
Fifa17Identity {
|
||||
item_id: 100000427,
|
||||
asset_id: 5001,
|
||||
resource_id: 5001,
|
||||
rareflag: 1,
|
||||
},
|
||||
),
|
||||
]));
|
||||
let mut input = one_slot_input(&owned, SquadExtInput::Fresh(fresh_ext()));
|
||||
input.manager = Some(owned_item("oc-mgr", "fifa17_mgr"));
|
||||
let SquadProjection::Projected(v) = project_squad(&input, &ident, &ent()).unwrap() else {
|
||||
panic!("expected Projected");
|
||||
};
|
||||
assert_eq!(
|
||||
v["manager"],
|
||||
json!([{ "id": 100000427, "dream": false }]),
|
||||
"manager is the ownership-backed wire ref, resolved from the owned item"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn absent_manager_projects_empty_array_never_fabricated() {
|
||||
let mut owned = HashMap::new();
|
||||
owned.insert("oc1".to_string(), owned_item("oc1", "card_x"));
|
||||
let ident = TableIdentity(HashMap::from([(
|
||||
"oc1".to_string(),
|
||||
Fifa17Identity {
|
||||
item_id: 100000042,
|
||||
asset_id: 20801,
|
||||
resource_id: 20801,
|
||||
rareflag: 1,
|
||||
},
|
||||
)]));
|
||||
// one_slot_input leaves manager: None.
|
||||
let input = one_slot_input(&owned, SquadExtInput::Fresh(fresh_ext()));
|
||||
let SquadProjection::Projected(v) = project_squad(&input, &ident, &ent()).unwrap() else {
|
||||
panic!("expected Projected");
|
||||
};
|
||||
assert_eq!(
|
||||
v["manager"],
|
||||
json!([]),
|
||||
"no manager assignment => empty array, nothing fabricated"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user