feat(import-fifa17): seed unopenedPackIds as Core entitlements
Carry unopenedPackIds through the importer: Profile model -> Report -> ApplyPlan. GenericImportRequest now emits entitlements[] (one definition_id per unopened pack instance, order+duplicates preserved), which Core's import seeds as unconsumed packs rows in the same transaction. Closes the economy import gap so a migrated profile's unopened packs become Core entitlements (feeding purchasegroup/credits/userMassInfo). Idempotency unchanged (import fingerprint). +1 test; 26 pass, clippy -D warnings clean.
This commit is contained in:
@@ -105,6 +105,15 @@ pub struct GenericImportRequest {
|
|||||||
pub owned: Vec<GenericOwned>,
|
pub owned: Vec<GenericOwned>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub squad: Option<GenericSquad>,
|
pub squad: Option<GenericSquad>,
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub entitlements: Vec<GenericEntitlement>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// One unconsumed entitlement to seed into Core (a source `unopenedPackIds`
|
||||||
|
/// entry). `definition_id` is the pack id as text; Core stores it verbatim.
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct GenericEntitlement {
|
||||||
|
pub definition_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// One preserved (opaque OwnedItemId ↔ source wire id) mapping to install into
|
/// One preserved (opaque OwnedItemId ↔ source wire id) mapping to install into
|
||||||
@@ -231,6 +240,13 @@ pub fn plan_apply(
|
|||||||
},
|
},
|
||||||
owned,
|
owned,
|
||||||
squad,
|
squad,
|
||||||
|
entitlements: report
|
||||||
|
.unopened_pack_ids
|
||||||
|
.iter()
|
||||||
|
.map(|id| GenericEntitlement {
|
||||||
|
definition_id: id.to_string(),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(ApplyPlan {
|
Ok(ApplyPlan {
|
||||||
|
|||||||
@@ -632,6 +632,8 @@ pub struct Report {
|
|||||||
pub definitions: DefinitionPlan,
|
pub definitions: DefinitionPlan,
|
||||||
pub identity: IdentityPlan,
|
pub identity: IdentityPlan,
|
||||||
pub squad: SquadCoverage,
|
pub squad: SquadCoverage,
|
||||||
|
/// Unconsumed pack entitlements to seed (from `unopenedPackIds`).
|
||||||
|
pub unopened_pack_ids: Vec<i64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Report {
|
impl Report {
|
||||||
@@ -729,6 +731,7 @@ pub fn analyze(
|
|||||||
definitions,
|
definitions,
|
||||||
identity,
|
identity,
|
||||||
squad,
|
squad,
|
||||||
|
unopened_pack_ids: profile.unopened_pack_ids.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ pub struct Profile {
|
|||||||
pub items: Vec<Item>,
|
pub items: Vec<Item>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub squads: Vec<Squad>,
|
pub squads: Vec<Squad>,
|
||||||
|
/// Unconsumed pack entitlements (`unopenedPackIds`), seeded into Core as
|
||||||
|
/// generic entitlements on import.
|
||||||
|
#[serde(rename = "unopenedPackIds", default)]
|
||||||
|
pub unopened_pack_ids: Vec<i64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Profile {
|
impl Profile {
|
||||||
|
|||||||
@@ -462,6 +462,29 @@ fn plan_apply_builds_generic_request_mappings_and_squad() {
|
|||||||
.any(|s| s.owned_item_id == cap_owned && s.is_captain));
|
.any(|s| s.owned_item_id == cap_owned && s.is_captain));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn plan_apply_seeds_entitlements_from_unopened_packs() {
|
||||||
|
let items = [player(100000001, 20801, 20801, 94)];
|
||||||
|
let json = format!(
|
||||||
|
r#"{{"personaId":33068179,"personaName":"CAGE","clubName":"OpenFUT","clubAbbr":"OFC",
|
||||||
|
"coins":1000,"nextItemId":100000500,"items":[{}],"squads":[],"unopenedPackIds":[70,70,5]}}"#,
|
||||||
|
items.join(",")
|
||||||
|
);
|
||||||
|
let prof = Profile::from_json_str(&json).unwrap();
|
||||||
|
let report = analyze(&prof, &roster(), &entities(), &none());
|
||||||
|
assert_eq!(report.unopened_pack_ids, [70, 70, 5]);
|
||||||
|
let raw: serde_json::Value = serde_json::from_str(&json).unwrap();
|
||||||
|
let plan = plan_apply(&report, &raw, "fp-ent").unwrap();
|
||||||
|
let defs: Vec<&str> = plan
|
||||||
|
.request
|
||||||
|
.entitlements
|
||||||
|
.iter()
|
||||||
|
.map(|e| e.definition_id.as_str())
|
||||||
|
.collect();
|
||||||
|
// One entitlement per unopened pack instance, order preserved (dup 70 kept).
|
||||||
|
assert_eq!(defs, ["70", "70", "5"]);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn plan_apply_refuses_when_blockers_present() {
|
fn plan_apply_refuses_when_blockers_present() {
|
||||||
// unapproved same-resourceId conflict -> blocker.
|
// unapproved same-resourceId conflict -> blocker.
|
||||||
|
|||||||
Reference in New Issue
Block a user