Add committing of the db operations into the migration lifecycle

This commit is contained in:
Oliver 2026-04-20 21:25:48 -06:00
parent 85e3838396
commit b0a3d972f0
4 changed files with 84 additions and 30 deletions

View file

@ -133,10 +133,13 @@
"missing-id": "An ID must be provided", "missing-id": "An ID must be provided",
"invalid-socket": "Invalid socket data received, this means a module or system bug is present.", "invalid-socket": "Invalid socket data received, this means a module or system bug is present.",
"unknown-socket-event": "An unknown socket event was received: {event}", "unknown-socket-event": "An unknown socket event was received: {event}",
"malformed-socket-payload": "Socket event \"{event}\" received with malformed payload. Details: {details}" },
"warn": {
"migration-in-progress": "Applying data migrations for version {version}. Please do NOT refresh the window while this warning is present."
}, },
"success": { "success": {
"saved-default-attributes": "Successfully saved default Actor attributes" "saved-default-attributes": "Successfully saved default Actor attributes",
"migration-successful": "Data migrations for version {version} were successful."
} }
} }
} }

View file

@ -34,10 +34,14 @@ export class TAFActor extends Actor {
this.#sortedTypes = null; this.#sortedTypes = null;
}; };
static migrateData(data, ...args) { static migrateData(data, options) {
if (options.partial) { return }
console.log(`Actor#migrateData`, foundry.utils.deepClone(data), options);
if (Object.keys(data.system?.attr ?? {}).length > 0) { if (Object.keys(data.system?.attr ?? {}).length > 0) {
console.log(`attributes exist`)
setProperty(data, `flags.${__ID__}.convertAttributesIntoItems`, true); setProperty(data, `flags.${__ID__}.convertAttributesIntoItems`, true);
}; };
return data;
}; };
// #endregion Lifecycle // #endregion Lifecycle

View file

@ -23,10 +23,7 @@ export async function migrateCollection(
options = {} options = {}
) { ) {
const toMigrate = collection const toMigrate = collection
.filter(doc => { .filter(doc => doc.getFlag(__ID__, flag))
console.log(`toMigrate.filter doc`, doc);
return doc.getFlag(__ID__, flag)
})
.map(doc => { .map(doc => {
const update = convertor(doc) ?? {}; const update = convertor(doc) ?? {};
update[`_id`] = doc._id; update[`_id`] = doc._id;
@ -41,8 +38,20 @@ export async function migrateCollection(
return update; return update;
}) })
.filter(data => !!data); .filter(data => !!data);
// update in increments of 100
// TODO: optionally return an array of DB operations for modifyBatch if (!options.update) {
return [{
action: `update`,
broadcast: true,
documentName: collection.documentName,
updates: toMigrate,
noHook: true,
pack: options.pack,
parent: options.parent,
}];
};
// Modify in batches of 100
const batches = Math.ceil(toMigrate.length / 100); const batches = Math.ceil(toMigrate.length / 100);
for (let i = 0; i < batches; i++) { for (let i = 0; i < batches; i++) {
const updateData = toMigrate.slice(i * 100, (i + 1) * 100); const updateData = toMigrate.slice(i * 100, (i + 1) * 100);
@ -65,9 +74,9 @@ export async function migrateCollection(
* @param pack The CompendiumPack document * @param pack The CompendiumPack document
* @returns {boolean} Whether or not the pack should be migrated * @returns {boolean} Whether or not the pack should be migrated
*/ */
export function shouldMigrateCompendium(pack) { export function shouldMigrateCompendium(pack, types = [`Actor`, `Item`]) {
// We only care about actor and item migrations // We only care about actor and item migrations
if (!["Actor", "Item"].includes(pack.documentName)) return false; if (!types.includes(pack.documentName)) return false;
// World compendiums should all be migrated, system ones should never by migrated // World compendiums should all be migrated, system ones should never by migrated
if (pack.metadata.packageType === "world") return true; if (pack.metadata.packageType === "world") return true;

View file

@ -3,9 +3,24 @@ import { migrateCollection, shouldMigrateCompendium } from "./utils.mjs";
const flag = `convertAttributesIntoItems`; const flag = `convertAttributesIntoItems`;
const operations = []; const operations = [];
let compendiumOperations = [];
export async function migrateTo3_0_0() { export async function migrateTo3_0_0() {
Logger.debug(`Starting v3.0.0 data migration`); Logger.debug(`Starting v3.0.0 data migration`);
const packsToMigrate = game.packs.filter(
(pack) => shouldMigrateCompendium(pack, [`Actor`]),
);
const intervalSize = 1 / (packsToMigrate.length + 1);
const warning = ui.notifications.warn(
"taf.notifs.warn.migration-in-progress",
{
format: { version: `v3.0.0` },
progress: true,
permanent: true,
console: false,
},
);
operations.push( operations.push(
...await migrateCollection( ...await migrateCollection(
@ -15,30 +30,41 @@ export async function migrateTo3_0_0() {
{ update: false, }, { update: false, },
), ),
); );
warning.update({ pct: warning.pct + intervalSize });
// for (const pack of game.packs) { for (const pack of packsToMigrate) {
// if ( await pack.getDocuments();
// pack.metadata.type !== "Actor"
// || !shouldMigrateCompendium(pack)
// ) {
// continue;
// };
// await pack.getDocuments(); const wasLocked = pack.config.locked;
if (wasLocked) pack.configure({ locked: false });
// // TODO: unlock compendium if required then re-lock after finishing compendiumOperations.push(
// await migrateCollection( ...await migrateCollection(
// pack, pack,
// flag, flag,
// handleMigratingActor, handleMigratingActor,
// { pack }, { pack, update: false, },
// ); ),
// }; );
// foundry.documents.modifyBatch(compendiumOperations);
console.log(`compendiumOperations`, compendiumOperations);
if (wasLocked) await pack.configure({ locked: true });
compendiumOperations = [];
warning.update({ pct: warning.pct + intervalSize });
};
// TODO: re-lock packs here?
warning.update({ pct: 1 });
// TODO: create the item documents (batch them if possible) // TODO: create the item documents (batch them if possible)
Logger.debug(`Finished v3.0.0 migration, resulting operations:`); Logger.debug(`Finished v3.0.0 migration, resulting operations:`);
console.log(operations); console.log(operations);
// Use: foundry.documents.modifyBatch // Use: foundry.documents.modifyBatch
// await foundry.documents.modifyBatch(operations);
}; };
function handleMigratingActor(actor) { function handleMigratingActor(actor) {
@ -48,16 +74,27 @@ function handleMigratingActor(actor) {
action: `create`, action: `create`,
documentName: `Item`, documentName: `Item`,
parent: actor, parent: actor,
noHook: true,
data: [], data: [],
}; };
const attrs = actor.system.attr; const attrs = actor.system?.attr ?? {};
for (const [ key, attr ] of Object.entries(attrs)) { for (const [ key, attr ] of Object.entries(attrs)) {
operation.data.push(convertToItem(key, attr)); operation.data.push(convertToItem(key, attr));
}; };
operations.push(operation);
return null; // No items to create, don't queue the operation
if (operation.data.length > 0) {
if (actor.inCompendium) {
compendiumOperations.push(operation);
} else {
operations.push(operation);
};
};
return {
"system.attr": _del,
};
}; };
function convertToItem(key, attr) { function convertToItem(key, attr) {
@ -68,6 +105,7 @@ function convertToItem(key, attr) {
key, key,
value: attr.value, value: attr.value,
max: attr.isRange ? attr.max : null, max: attr.isRange ? attr.max : null,
aboveTheFold: true,
}, },
}; };
}; };