refactor: update each document individually during migrations

This commit is contained in:
Johannes Loher 2023-07-09 20:53:18 +02:00
parent e536a6fbda
commit 124824da83
Signed by: saluu
GPG key ID: 7CB0A9FB553DA045
10 changed files with 255 additions and 375 deletions

View file

@ -2,24 +2,18 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { import { getSceneMigrator, migrateCollection, migrateCompendiums, getCompendiumMigrator } from "./migrationHelpers.js";
getCompendiumMigrator,
getSceneUpdateDataGetter,
migrateActors,
migrateCompendiums,
migrateScenes,
} from "./migrationHelpers";
/** @type {import("./migration").Migration["migrate"]} */ /** @type {import("./migration.js").Migration["migrate"]} */
async function migrate() { async function migrate() {
await migrateActors(getActorUpdateData); await migrateCollection(game.actors, migrateActor);
await migrateScenes(getSceneUpdateData); await migrateCollection(game.scenes, migrateScene);
await migrateCompendiums(migrateCompendium); await migrateCompendiums(migrateCompendium);
} }
/** @type {import("./migrationHelpers").ActorUpdateDataGetter} */ /** @type {import('./migrationHelpers.js').Migrator<Actor>} */
function getActorUpdateData() { async function migrateActor(actor) {
const updateData = { await actor.update({
system: { system: {
combatValues: [ combatValues: [
"hitPoints", "hitPoints",
@ -35,14 +29,14 @@ function getActorUpdateData() {
return acc; return acc;
}, {}), }, {}),
}, },
}; });
return updateData;
} }
const getSceneUpdateData = getSceneUpdateDataGetter(getActorUpdateData); const migrateScene = getSceneMigrator(migrateActor);
const migrateCompendium = getCompendiumMigrator({ getActorUpdateData, getSceneUpdateData });
/** @type {import("./migration").Migration} */ const migrateCompendium = getCompendiumMigrator({ migrateActor, migrateScene });
/** @type {import("./migration.js").Migration} */
export const migration = { export const migration = {
migrate, migrate,
migrateCompendium, migrateCompendium,

View file

@ -3,37 +3,36 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { import {
getActorUpdateDataGetter, getSceneMigrator,
getCompendiumMigrator, migrateCollection,
getSceneUpdateDataGetter,
migrateActors,
migrateCompendiums, migrateCompendiums,
migrateItems, getCompendiumMigrator,
migrateScenes, getActorMigrator,
} from "./migrationHelpers"; } from "./migrationHelpers.js";
/** @type {import("./migration").Migration["migrate"]} */ /** @type {import("./migration.js").Migration["migrate"]} */
async function migrate() { async function migrate() {
await migrateItems(getItemUpdateData); await migrateCollection(game.items, migrateItem);
await migrateActors(getActorUpdateData); await migrateCollection(game.actors, migrateActor);
await migrateScenes(getSceneUpdateData); await migrateCollection(game.scenes, migrateScene);
await migrateCompendiums(migrateCompendium); await migrateCompendiums(migrateCompendium);
} }
/** @type {import("./migrationHelpers").ItemUpdateDataGetter} */ /** @type {import('./migrationHelpers.js').Migrator<Item>} */
function getItemUpdateData(itemData) { async function migrateItem(item) {
if (!["equipment", "trinket"].includes(itemData.type ?? "")) return undefined; if (item.type === "equipment" || item.type === "trinket") {
return { type: itemData.type === "equipment" ? "loot" : "equipment" }; await item.update({ type: item.type === "equipment" ? "loot" : "equipment" });
}
} }
const getActorUpdateData = getActorUpdateDataGetter(getItemUpdateData); const migrateActor = getActorMigrator(migrateItem);
const getSceneUpdateData = getSceneUpdateDataGetter(getActorUpdateData); const migrateScene = getSceneMigrator(migrateActor);
const migrateCompendium = getCompendiumMigrator( const migrateCompendium = getCompendiumMigrator(
{ getItemUpdateData, getActorUpdateData, getSceneUpdateData }, { migrateItem, migrateActor, migrateScene },
{ migrateToTemplateEarly: false }, { migrateToTemplateEarly: false },
); );
/** @type {import("./migration").Migration} */ /** @type {import("./migration.js").Migration} */
export const migration = { export const migration = {
migrate, migrate,
migrateCompendium, migrateCompendium,

View file

@ -3,41 +3,36 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { import {
getActorUpdateDataGetter, getSceneMigrator,
getCompendiumMigrator, migrateCollection,
getSceneUpdateDataGetter,
migrateActors,
migrateCompendiums, migrateCompendiums,
migrateItems, getCompendiumMigrator,
migrateScenes, getActorMigrator,
} from "./migrationHelpers"; } from "./migrationHelpers.js";
/** @type {import("./migration").Migration["migrate"]} */ /** @type {import("./migration.js").Migration["migrate"]} */
async function migrate() { async function migrate() {
await migrateItems(getItemUpdateData); await migrateCollection(game.items, migrateItem);
await migrateActors(getActorUpdateData); await migrateCollection(game.actors, migrateActor);
await migrateScenes(getSceneUpdateData); await migrateCollection(game.scenes, migrateScene);
await migrateCompendiums(migrateCompendium); await migrateCompendiums(migrateCompendium);
} }
/** @type {import("./migrationHelpers").ItemUpdateDataGetter} */ /** @type {import("./migrationHelpers.js").Migrator<Item>} */
function getItemUpdateData(itemData) { async function migrateItem(item) {
if (!["loot"].includes(itemData.type ?? "")) return undefined; if (item.type === "loot") {
return { await item.update({ system: { "-=equipped": null } });
system: { }
"-=equipped": null,
},
};
} }
const getActorUpdateData = getActorUpdateDataGetter(getItemUpdateData); const migrateActor = getActorMigrator(migrateItem);
const getSceneUpdateData = getSceneUpdateDataGetter(getActorUpdateData); const migrateScene = getSceneMigrator(migrateActor);
const migrateCompendium = getCompendiumMigrator( const migrateCompendium = getCompendiumMigrator(
{ getItemUpdateData, getActorUpdateData }, { migrateItem, migrateActor, migrateScene },
{ migrateToTemplateEarly: false }, { migrateToTemplateEarly: false },
); );
/** @type {import("./migration").Migration} */ /** @type {import("./migration.js").Migration} */
export const migration = { export const migration = {
migrate, migrate,
migrateCompendium, migrateCompendium,

View file

@ -3,45 +3,44 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { import {
getActorUpdateDataGetter, getSceneMigrator,
getCompendiumMigrator, migrateCollection,
getSceneUpdateDataGetter,
migrateActors,
migrateCompendiums, migrateCompendiums,
migrateItems, getCompendiumMigrator,
migrateScenes, getActorMigrator,
} from "./migrationHelpers"; } from "./migrationHelpers.js";
/** @type {import("./migration").Migration["migrate"]} */ /** @type {import("./migration.js").Migration["migrate"]} */
async function migrate() { async function migrate() {
await migrateItems(getItemUpdateData); await migrateCollection(game.items, migrateItem);
await migrateActors(getActorUpdateData); await migrateCollection(game.actors, migrateActor);
await migrateScenes(getSceneUpdateData); await migrateCollection(game.scenes, migrateScene);
await migrateCompendiums(migrateCompendium); await migrateCompendiums(migrateCompendium);
} }
/** @type {import("./migrationHelpers").ItemUpdateDataGetter} */ /** @type {import('./migrationHelpers.js').Migrator<Item>} */
function getItemUpdateData(itemData) { async function migrateItem(item) {
if (itemData.type !== "spell") return; if (item.type === "spell") {
const cooldownDurationUnit = itemData.system?.cooldownDuration.unit; const cooldownDurationUnit = item.system?.cooldownDuration.unit;
await item.update({
const updateData = { system: {
system: { "-=scrollPrice": null,
"-=scrollPrice": null, minimumLevels: { healer: null, wizard: null, sorcerer: null },
minimumLevels: { healer: null, wizard: null, sorcerer: null }, cooldownDuration: {
cooldownDuration: { unit: cooldownDurationUnit === "custom" ? "rounds" : cooldownDurationUnit,
unit: cooldownDurationUnit === "custom" ? "rounds" : cooldownDurationUnit, },
}, },
}, });
}; }
return updateData; return updateData;
} }
const getActorUpdateData = getActorUpdateDataGetter(getItemUpdateData); const migrateActor = getActorMigrator(migrateItem);
const getSceneUpdateData = getSceneUpdateDataGetter(getActorUpdateData); const migrateScene = getSceneMigrator(migrateActor);
const migrateCompendium = getCompendiumMigrator({ getItemUpdateData, getActorUpdateData, getSceneUpdateData }); const migrateCompendium = getCompendiumMigrator({ migrateItem, migrateActor, migrateScene });
/** @type {import("./migration").Migration} */ /** @type {import("./migration.js").Migration} */
export const migration = { export const migration = {
migrate, migrate,
migrateCompendium, migrateCompendium,

View file

@ -3,14 +3,12 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { import {
getActorUpdateDataGetter, getSceneMigrator,
getCompendiumMigrator, migrateCollection,
getSceneUpdateDataGetter,
migrateActors,
migrateCompendiums, migrateCompendiums,
migrateItems, getCompendiumMigrator,
migrateScenes, getActorMigrator,
} from "./migrationHelpers"; } from "./migrationHelpers.js";
const secondsPerRound = 5; const secondsPerRound = 5;
const secondsPerMinute = 60; const secondsPerMinute = 60;
@ -21,27 +19,22 @@ const hoursPerDay = 24;
const roundsPerDay = hoursPerDay / roundsPerHour; const roundsPerDay = hoursPerDay / roundsPerHour;
const secondsPerDay = secondsPerMinute * minutesPerHour * hoursPerDay; const secondsPerDay = secondsPerMinute * minutesPerHour * hoursPerDay;
/** @type {import("./migration").Migration["migrate"]} */ /** @type {import("./migration.js").Migration["migrate"]} */
async function migrate() { async function migrate() {
await migrateItems(getItemUpdateData); await migrateCollection(game.items, migrateItem);
await migrateActors(getActorUpdateData); await migrateCollection(game.actors, migrateActor);
await migrateScenes(getSceneUpdateData); await migrateCollection(game.scenes, migrateScene);
await migrateCompendiums(migrateCompendium); await migrateCompendiums(migrateCompendium);
} }
/** @type {import("./migrationHelpers").ItemUpdateDataGetter} */ /** @type {import('./migrationHelpers.js').Migrator<Item>} */
function getItemUpdateData(itemData) { async function migrateItem(item) {
if (itemData.type !== "spell") return; if (item.type === "spell") {
const cooldownDurationUnit = itemData.system?.cooldownDuration.unit; const cooldownDurationUnit = item.system?.cooldownDuration.unit;
const cooldownDurationValue = itemData.system?.cooldownDuration.value; const cooldownDurationValue = item.system?.cooldownDuration.value;
const cooldownDuration = migrateCooldownDuration(cooldownDurationValue, cooldownDurationUnit); const cooldownDuration = migrateCooldownDuration(cooldownDurationValue, cooldownDurationUnit);
await item.update({ system: { cooldownDuration } });
const updateData = { }
system: {
cooldownDuration,
},
};
return updateData;
} }
function migrateCooldownDuration(cooldownDurationValue = "", cooldownDurationUnit = "") { function migrateCooldownDuration(cooldownDurationValue = "", cooldownDurationUnit = "") {
@ -114,11 +107,11 @@ function getRounds(unit, value) {
} }
} }
const getActorUpdateData = getActorUpdateDataGetter(getItemUpdateData); const migrateActor = getActorMigrator(migrateItem);
const getSceneUpdateData = getSceneUpdateDataGetter(getActorUpdateData); const migrateScene = getSceneMigrator(migrateActor);
const migrateCompendium = getCompendiumMigrator({ getItemUpdateData, getActorUpdateData, getSceneUpdateData }); const migrateCompendium = getCompendiumMigrator({ migrateItem, migrateActor, migrateScene });
/** @type {import("./migration").Migration} */ /** @type {import("./migration.js").Migration} */
export const migration = { export const migration = {
migrate, migrate,
migrateCompendium, migrateCompendium,

View file

@ -3,42 +3,37 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { import {
getActorUpdateDataGetter, getSceneMigrator,
getCompendiumMigrator, migrateCollection,
getSceneUpdateDataGetter,
migrateActors,
migrateCompendiums, migrateCompendiums,
migrateItems, getCompendiumMigrator,
migrateScenes, getActorMigrator,
} from "./migrationHelpers"; } from "./migrationHelpers.js";
/** @type {import("./migration").Migration["migrate"]} */ /** @type {import("./migration.js").Migration["migrate"]} */
async function migrate() { async function migrate() {
await migrateItems(getItemUpdateData); await migrateCollection(game.items, migrateItem);
await migrateActors(getActorUpdateData); await migrateCollection(game.actors, migrateActor);
await migrateScenes(getSceneUpdateData); await migrateCollection(game.scenes, migrateScene);
await migrateCompendiums(migrateCompendium); await migrateCompendiums(migrateCompendium);
} }
/** @type {import("./migrationHelpers").ItemUpdateDataGetter} */ /** @type {import('./migrationHelpers.js').Migrator<Item>} */
function getItemUpdateData(itemData) { async function migrateItem(item) {
if (itemData.type !== "spell") return; if (item.type === "spell") {
const spellCategory = itemData.system?.spellCategory; const spellCategory = item.system?.spellCategory;
const spellGroups = migrateSpellCategory(spellCategory); const spellGroups = migrateSpellCategory(spellCategory);
const bonus = itemData.system?.bonus;
// @ts-expect-error bonus is removed with this migration const spellModifier = migrateBonus(bonus);
const bonus = itemData.system?.bonus; await item.update({
const spellModifier = migrateBonus(bonus); system: {
spellGroups,
const updateData = { "-=spellCategory": null,
system: { spellModifier,
spellGroups, "-=bonus": null,
"-=spellCategory": null, },
spellModifier, });
"-=bonus": null, }
},
};
return updateData;
} }
/** /**
@ -116,11 +111,11 @@ function migrateBonus(bonus) {
return spellModifier; return spellModifier;
} }
const getActorUpdateData = getActorUpdateDataGetter(getItemUpdateData); const migrateActor = getActorMigrator(migrateItem);
const getSceneUpdateData = getSceneUpdateDataGetter(getActorUpdateData); const migrateScene = getSceneMigrator(migrateActor);
const migrateCompendium = getCompendiumMigrator({ getItemUpdateData, getActorUpdateData, getSceneUpdateData }); const migrateCompendium = getCompendiumMigrator({ migrateItem, migrateActor, migrateScene });
/** @type {import("./migration").Migration} */ /** @type {import("./migration.js").Migration} */
export const migration = { export const migration = {
migrate, migrate,
migrateCompendium, migrateCompendium,

View file

@ -3,39 +3,33 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { import {
getActorUpdateDataGetter, getSceneMigrator,
getCompendiumMigrator, migrateCollection,
getSceneUpdateDataGetter,
migrateActors,
migrateCompendiums, migrateCompendiums,
migrateItems, getCompendiumMigrator,
migrateScenes, getActorMigrator,
} from "./migrationHelpers"; } from "./migrationHelpers.js";
/** @type {import("./migration").Migration["migrate"]} */ /** @type {import("./migration.js").Migration["migrate"]} */
async function migrate() { async function migrate() {
await migrateItems(getItemUpdateData); await migrateCollection(game.items, migrateItem);
await migrateActors(getActorUpdateData); await migrateCollection(game.actors, migrateActor);
await migrateScenes(getSceneUpdateData); await migrateCollection(game.scenes, migrateScene);
await migrateCompendiums(migrateCompendium); await migrateCompendiums(migrateCompendium);
} }
/** @type {import("./migrationHelpers").ItemUpdateDataGetter} */ /** @type {import('./migrationHelpers.js').Migrator<Item>} */
function getItemUpdateData(itemData) { async function migrateItem(item) {
if (itemData.type !== "spell") return; if (item.type === "spell") {
await item.update({ system: { allowsDefense: false } });
return { }
system: {
allowsDefense: false,
},
};
} }
const getActorUpdateData = getActorUpdateDataGetter(getItemUpdateData); const migrateActor = getActorMigrator(migrateItem);
const getSceneUpdateData = getSceneUpdateDataGetter(getActorUpdateData); const migrateScene = getSceneMigrator(migrateActor);
const migrateCompendium = getCompendiumMigrator({ getItemUpdateData, getActorUpdateData, getSceneUpdateData }); const migrateCompendium = getCompendiumMigrator({ migrateItem, migrateActor, migrateScene });
/** @type {import("./migration").Migration} */ /** @type {import("./migration.js").Migration} */
export const migration = { export const migration = {
migrate, migrate,
migrateCompendium, migrateCompendium,

View file

@ -3,28 +3,27 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { import {
getActorUpdateDataGetter, getSceneMigrator,
getCompendiumMigrator, migrateCollection,
getItemUpdateDataGetter,
getSceneUpdateDataGetter,
migrateActors,
migrateCompendiums, migrateCompendiums,
migrateItems, getCompendiumMigrator,
migrateScenes, getActorMigrator,
} from "./migrationHelpers"; getItemMigrator,
} from "./migrationHelpers.js";
/** @type {import("./migration").Migration["migrate"]} */ /** @type {import("./migration.js").Migration["migrate"]} */
async function migrate() { async function migrate() {
await migrateItems(getItemUpdateData); await migrateCollection(game.items, migrateItem);
await migrateActors(getActorUpdateData); await migrateCollection(game.actors, migrateActor);
await migrateScenes(getSceneUpdateData); await migrateCollection(game.scenes, migrateScene);
await migrateCompendiums(migrateCompendium); await migrateCompendiums(migrateCompendium);
} }
/** @type {import("./migrationHelpers").EffectUpdateDataGetter} */ /** @type {import('./migrationHelpers.js').Migrator<ActiveEffect>} */
function getEffectUpdateData(effectData) { async function migrateActiveEffect(activeEffect) {
const data = foundry.utils.deepClone(effectData); const data = activeEffect.toObject();
let hasUpdates = false; let hasUpdates = false;
if ("changes" in data) { if ("changes" in data) {
for (const change of data.changes) { for (const change of data.changes) {
const newValue = change.value.replaceAll(/@data\./g, "@system."); const newValue = change.value.replaceAll(/@data\./g, "@system.");
@ -45,16 +44,16 @@ function getEffectUpdateData(effectData) {
} }
} }
if (hasUpdates) { if (hasUpdates) {
return data; await activeEffect.update(data);
} }
} }
const getItemUpdateData = getItemUpdateDataGetter(getEffectUpdateData); const migrateItem = getItemMigrator(migrateActiveEffect);
const getActorUpdateData = getActorUpdateDataGetter(getItemUpdateData, getEffectUpdateData); const migrateActor = getActorMigrator(migrateItem, migrateActiveEffect);
const getSceneUpdateData = getSceneUpdateDataGetter(getActorUpdateData); const migrateScene = getSceneMigrator(migrateActor);
const migrateCompendium = getCompendiumMigrator({ getItemUpdateData, getActorUpdateData, getSceneUpdateData }); const migrateCompendium = getCompendiumMigrator({ migrateItem, migrateActor, migrateScene });
/** @type {import("./migration").Migration} */ /** @type {import("./migration.js").Migration} */
export const migration = { export const migration = {
migrate, migrate,
migrateCompendium, migrateCompendium,

View file

@ -2,17 +2,17 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { notifications } from "../ui/notifications"; import { notifications } from "../ui/notifications.js";
import { logger } from "../utils/logger"; import { logger } from "../utils/logger.js";
import { getGame } from "../utils/utils"; import { getGame } from "../utils/utils.js";
import { migration as migration001 } from "./001"; import { migration as migration001 } from "./001.js";
import { migration as migration002 } from "./002"; import { migration as migration002 } from "./002.js";
import { migration as migration003 } from "./003"; import { migration as migration003 } from "./003.js";
import { migration as migration004 } from "./004"; import { migration as migration004 } from "./004.js";
import { migration as migration005 } from "./005"; import { migration as migration005 } from "./005.js";
import { migration as migration006 } from "./006"; import { migration as migration006 } from "./006.js";
import { migration as migration007 } from "./007"; import { migration as migration007 } from "./007.js";
import { migration as migration008 } from "./008"; import { migration as migration008 } from "./008.js";
/** /**
* Perform migrations. * Perform migrations.

View file

@ -2,82 +2,95 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { DS4Actor } from "../documents/actor/actor"; import { DS4Actor } from "../documents/actor/actor.js";
import { DS4Item } from "../documents/item/item"; import { DS4Item } from "../documents/item/item.js";
import { logger } from "../utils/logger"; import { logger } from "../utils/logger.js";
import { getGame } from "../utils/utils"; import { getGame } from "../utils/utils.js";
/** @typedef {(effectData: object) => Record<string, unknown> | undefined} EffectUpdateDataGetter */
/** @typedef {(itemData: object) => Record<string, unknown> | undefined} ItemUpdateDataGetter */
/** /**
* Migrate world items. * @template T
* @param {ItemUpdateDataGetter} getItemUpdateData A function for getting the update data for a given item data object * @typedef {(document: T) => Promise<void>} Migrator
* @returns {Promise<void>} A promise that resolves once the migration is complete
*/ */
export async function migrateItems(getItemUpdateData) {
for (const item of getGame().items ?? []) {
try {
const updateData = getItemUpdateData(item.toObject());
if (updateData) {
logger.info(`Migrating Item document ${item.name} (${item.id})`);
await item.update(updateData), { enforceTypes: false };
}
} catch (err) {
logger.error(`Error during migration of Item document ${item.name} (${item.id}), continuing anyways.`, err);
}
}
}
/** @typedef {(actorData: object>) => Record<string, unknown> | undefined} ActorUpdateDataGetter */
/** /**
* Migrate world actors. * Migrate a collection.
* @param {ActorUpdateDataGetter} getActorUpdateData A function for getting the update data for a given actor data object * @template T
* @param {WorldCollection} collection
* @param {Migrator<T>} migrateDocument
* @returns {Promise<void>} A promise that resolves once the migration is complete * @returns {Promise<void>} A promise that resolves once the migration is complete
*/ */
export async function migrateActors(getActorUpdateData) { export async function migrateCollection(collection, migrateDocument) {
for (const actor of getGame().actors ?? []) { const { documentName } = collection.constructor;
for (const document of collection) {
logger.info(`Migrating ${documentName} document ${document.name} (${document.id})`);
try { try {
const updateData = getActorUpdateData(actor.toObject()); await migrateDocument(document);
if (updateData) {
logger.info(`Migrating Actor document ${actor.name} (${actor.id})`);
await actor.update(updateData);
}
} catch (err) { } catch (err) {
logger.error( logger.error(
`Error during migration of Actor document ${actor.name} (${actor.id}), continuing anyways.`, `Error during migration of ${documentName} document ${document.name} (${document.id}), continuing anyways.`,
err, err,
); );
} }
} }
} }
/** @typedef {(scene: Scene) => Record<string, unknown> | undefined} SceneUpdateDataGetter */ /**
* @param {Migrator<ActiveEffect>} [migrateActiveEffect]
* @returns {Migrator<Scene>}
*/
export function getItemMigrator(migrateActiveEffect) {
/**
* @param {Item} item
*/
return async (item) => {
if (migrateActiveEffect) {
for (const effect of item.effects) {
await migrateActiveEffect(effect);
}
}
};
}
/** /**
* Migrate world scenes. * @param {Migrator<Item>} [migrateItem]
* @param {SceneUpdateDataGetter} getSceneUpdateData A function for getting the update data for a given scene data object * @param {Migrator<ActiveEffect>} [migrateActiveEffect]
* @returns {Promise<void>} A promise that resolves once the migration is complete * @returns {Migrator<Scene>}
*/ */
export async function migrateScenes(getSceneUpdateData) { export function getActorMigrator(migrateItem, migrateActiveEffect) {
for (const scene of getGame().scenes ?? []) { /**
try { * @param {Actor} actor
const updateData = getSceneUpdateData(scene); */
if (updateData) { return async (actor) => {
logger.info(`Migrating Scene document ${scene.name} (${scene.id})`); if (migrateItem) {
await scene.update(updateData); for (const item of actor.items) {
// We need to clear the old syntehtic actors from the cache await migrateItem(item);
scene.tokens.forEach((t) => (t._actor = null));
} }
} catch (err) {
logger.error(
`Error during migration of Scene document ${scene.name} (${scene.id}), continuing anyways.`,
err,
);
} }
} if (migrateActiveEffect) {
for (const effect of actor.effects) {
await migrateActiveEffect(effect);
}
}
};
}
/**
* @param {Migrator<Actor>} [migrateActor]
* @returns {Migrator<Scene>}
*/
export function getSceneMigrator(migrateActor) {
/**
* @param {Scene} scene
*/
return async (scene) => {
if (migrateActor) {
for (const token of scene.tokens) {
if (!token.actorLink && token.actor) {
await migrateActor(token.actor);
}
}
}
};
} }
/** @typedef {(pack: CompendiumCollection) => Promise<void>} CompendiumMigrator */ /** @typedef {(pack: CompendiumCollection) => Promise<void>} CompendiumMigrator */
@ -96,118 +109,20 @@ export async function migrateCompendiums(migrateCompendium) {
} }
/** /**
* Get a function to create item update data based on the given function to update embedded documents. * @typedef {object} Migrators
* @param {EffectUpdateDataGetter} [getEffectUpdateData] A function to generate effect update data * @property {Migrator<Item>} [migrateItem]
* @returns {ItemUpdateDataGetter} A function to get item update data * @property {Migrator<Actor>} [migrateActor]
*/ * @property {Migrator<Scene>} [migrateScene]
export function getItemUpdateDataGetter(getEffectUpdateData) {
return (itemData) => {
let hasEffectUpdates = false;
const effects = itemData.effects?.map((effectData) => {
const update = getEffectUpdateData(effectData);
if (update) {
hasEffectUpdates = true;
return foundry.utils.mergeObject(effectData, update, { inplace: false, performDeletions: true });
} else {
return effectData;
}
});
return hasEffectUpdates ? { effects } : undefined;
};
}
/**
* Get a function to create actor update data based on the given function to update embedded documents.
* @param {ItemUpdateDataGetter} [getItemUpdateData] A function to generate item update data
* @param {EffectUpdateDataGetter} [getEffectUpdateData] A function to generate effect update data
* @returns {ActorUpdateDataGetter} A function to get actor update data
*/
export function getActorUpdateDataGetter(getItemUpdateData, getEffectUpdateData) {
return (actorData) => {
let hasItemUpdates = false;
const items = actorData.items?.map((itemData) => {
const update = getItemUpdateData?.(itemData);
if (update) {
hasItemUpdates = true;
return foundry.utils.mergeObject(itemData, update, { inplace: false, performDeletions: true });
} else {
return itemData;
}
});
let hasEffectUpdates = false;
const effects = actorData.effects?.map((effectData) => {
const update = getEffectUpdateData?.(effectData);
if (update) {
hasEffectUpdates = true;
return foundry.utils.mergeObject(effectData, update, { inplace: false, performDeletions: true });
} else {
return effectData;
}
});
const result = {
items: hasItemUpdates ? items : undefined,
effects: hasEffectUpdates ? effects : undefined,
};
return hasItemUpdates | hasEffectUpdates ? result : undefined;
};
}
/**
* Get a function to create scene update data that adjusts the actors of the tokens of the scene according to the given function.
* @param {ActorUpdateDataGetter} [getItemUpdateData] The function to generate actor update data
* @returns {SceneUpdateDataGetter} A function to get scene update data
*/
export function getSceneUpdateDataGetter(getActorUpdateData) {
return (scene) => {
const tokens = scene.tokens.map((token) => {
const t = token.toObject();
if (!t.actorId || t.actorLink) {
t.actorData = {};
} else if (!getGame().actors?.has(t.actorId)) {
t.actorId = null;
t.actorData = {};
} else if (!t.actorLink) {
const actorData = foundry.utils.deepClone(t.actorData);
actorData.type = token.actor?.type;
const update = getActorUpdateData?.(actorData);
if (update !== undefined) {
["items", "effects"].forEach((embeddedName) => {
const embeddedUpdates = update[embeddedName];
if (!embeddedUpdates?.length) return;
const updates = new Map(embeddedUpdates.flatMap((u) => (u && u._id ? [[u._id, u]] : [])));
const originals = t.actorData[embeddedName];
if (!originals) return;
originals.forEach((original) => {
if (!original._id) return;
const update = updates.get(original._id);
if (update) foundry.utils.mergeObject(original, update, { performDeletions: true });
});
delete update[embeddedName];
});
foundry.utils.mergeObject(t.actorData, update);
}
}
return t;
});
return { tokens };
};
}
/**
* @typedef {object} UpdateDataGetters
* @property {ItemUpdateDataGetter} [getItemUpdateData]
* @property {ActorUpdateDataGetter} [getActorUpdateData]
* @property {SceneUpdateDataGetter} [getSceneUpdateData]
*/ */
/** /**
* Get a compendium migrator for the given update data getters. * Get a compendium migrator for the given migrators.
* @param {UpdateDataGetters} [updateDataGetters={}] The functions to use for getting update data * @param {Migrators} [migrators={}] The functions to use for getting update data
* @param {{migrateToTemplateEarly?: boolean}} [options={}] Additional options for the compendium migrator * @param {{migrateToTemplateEarly?: boolean}} [options={}] Additional options for the compendium migrator
* @returns {CompendiumMigrator} The resulting compendium migrator * @returns {CompendiumMigrator} The resulting compendium migrator
*/ */
export function getCompendiumMigrator( export function getCompendiumMigrator(
{ getItemUpdateData, getActorUpdateData, getSceneUpdateData } = {}, { migrateItem, migrateActor, migrateScene } = {},
{ migrateToTemplateEarly = true } = {}, { migrateToTemplateEarly = true } = {},
) { ) {
return async (pack) => { return async (pack) => {
@ -224,15 +139,12 @@ export function getCompendiumMigrator(
for (const doc of documents) { for (const doc of documents) {
try { try {
logger.info(`Migrating document ${doc.name} (${doc.id}) in compendium ${pack.collection}`); logger.info(`Migrating document ${doc.name} (${doc.id}) in compendium ${pack.collection}`);
if (doc instanceof DS4Item && getItemUpdateData) { if (doc instanceof DS4Item && migrateItem) {
const updateData = getItemUpdateData(doc.toObject()); await migrateItem(doc);
updateData && (await doc.update(updateData)); } else if (doc instanceof DS4Actor && migrateActor) {
} else if (doc instanceof DS4Actor && getActorUpdateData) { await migrateActor(doc);
const updateData = getActorUpdateData(doc.toObject()); } else if (doc instanceof Scene && migrateScene) {
updateData && (await doc.update(updateData)); await migrateScene(doc);
} else if (doc instanceof Scene && getSceneUpdateData) {
const updateData = getSceneUpdateData(doc);
updateData && (await doc.update(updateData));
} }
} catch (err) { } catch (err) {
logger.error( logger.error(