Added migration to the new parenting schema

This commit is contained in:
ThaumRystra
2023-09-29 17:59:17 +02:00
parent caea82bcc9
commit fb7413dba4
7 changed files with 142 additions and 5 deletions

View File

@@ -1,6 +1,6 @@
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
export default function cleanAt2(archive) {
export default function cleanArchiveAtCurrent(archive) {
archive.properties = archive.properties.map(prop => {
let cleanProp = prop;
try {

View File

@@ -1,6 +1,7 @@
import migrateTo1 from './migrateArchiveTo1';
import migrate1To2 from './migrateArchive1To2';
import cleanAt2 from './cleanArchiveAt2';
import migrate2To3 from './migrateArchive2To3';
import cleanAtCurrent from './cleanArchiveAtCurrent';
/* eslint no-fallthrough: "off" -- Using switch fallthrough to run all
migration steps after the current version of the file. */
@@ -15,7 +16,9 @@ export default function migrateArchive(archive) {
migrate1To2(archive);
// V2 of DiceCloud, Schema version 2
case 2:
cleanAt2(archive);
migrate2To3(archive);
case 3:
cleanAtCurrent(archive);
break;
default:
throw 'Archive version not supported';

View File

@@ -0,0 +1,16 @@
export default function migrate2To3(archive) {
archive.properties = archive.properties.map(prop => {
try {
prop.root = prop.ancestors[0];
if (!prop.root) {
throw 'Property has no root ancestor, will become orphaned'
}
if (prop.parent?.collection === 'creatureProperties') {
prop.parentId = prop.parent.id;
}
} catch (e) {
console.warn('Property migration 2 -> 3 failed: ', { propId: prop._id, error: e.message || e.reason || e.toString() });
}
return prop;
});
}

View File

@@ -0,0 +1,69 @@
import { migrateCollection } from './dbv3'
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
import { assert } from 'chai';
describe('dbv3 Migrate parenting structure', function () {
// We are going to be adding malformed docs to the collection, so allow any
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const collection = CreatureProperties as Mongo.Collection<any>;
this.beforeAll(async function () {
// Add only the required properties
await collection.removeAsync({});
// Add a property and a child as if they were in schema v2
// Use raw collection to bypass all schemas and validation
await collection.rawCollection().insertMany([
{
'_id': 'classId',
'type': 'class',
'parent': { 'collection': 'creatures', 'id': 'creatureId' },
'ancestors': [
{ 'collection': 'creatures', 'id': 'creatureId' }
],
'order': 0,
'tags': [],
}, {
'_id': 'noteId',
'type': 'note',
'parent': { 'collection': 'creatureProperties', 'id': 'classId' },
'ancestors': [
{ 'collection': 'creatures', 'id': 'creatureId' },
{ 'collection': 'creatureProperties', 'id': 'classId' }
],
'order': 1,
'tags': [],
},
]);
});
this.afterAll(async function () {
// Remove all the properties
await collection.removeAsync({});
});
it('Migrates parenting to the new schema', async function () {
// Migrate the collection
await migrateCollection('creatureProperties');
// Get the new documents
const parentDoc = await collection.findOneAsync('classId');
const childDoc = await collection.findOneAsync('noteId');
assert.deepEqual(
parentDoc.root, { collection: 'creatures', id: 'creatureId' },
'parent root should be the creature'
);
assert.deepEqual(
childDoc.root, { collection: 'creatures', id: 'creatureId' },
'child root should be the creature'
);
assert.doesNotHaveAnyKeys(parentDoc, ['parentId'], 'Parent should not have parentId set');
assert.equal(childDoc.parentId, 'classId', 'child parentId should match parent\'s id');
assert.equal(parentDoc.left, 0, 'Parent left should be its old order');
assert.equal(childDoc.left, 1, 'Child left should be its old order');
});
});

View File

@@ -0,0 +1,48 @@
import { Migrations } from 'meteor/percolate:migrations';
// Git version 2.0.59
// Database version 3
Migrations.add({
version: 3,
name: 'Separates creature property tags from library tags',
up() {
console.log('migrating up library nodes 2 -> 3');
migrateCollection('libraryNodes');
console.log('migrating up creature props 2 -> 3');
migrateCollection('creatureProperties');
console.log('New schema fields added, if it was done correctly remove the old fields manually');
},
down() {
throw 'Migrating from version 3 down to version 2 is not supported'
},
});
export function migrateCollection(collectionName: string) {
// @ts-expect-error Collection.get is not defined
const collection = Mongo.Collection.get(collectionName);
// Copy the parent id field and the root ancestor to the new structure
// Using the mongo aggregation API
return collection.rawCollection().updateMany({}, [
{
$addFields: {
'root': { $arrayElemAt: ['$ancestors', 0] },
'parentId': {
// Parent ID must refer to a document in the same collection, so remove the parent ID
// if the parent reference refers to a different collection
$cond: {
if: { $eq: [collectionName, '$parent.collection'] },
then: '$parent.id',
else: '$$REMOVE',
}
},
// Set left and right to current order so that order is maintained on the first re-build
// of the tree structure
'left': '$order',
'right': '$order',
}
},
]);
}

View File

@@ -1,6 +1,6 @@
{
"name": "dicecloud",
"version": "2.0.57",
"version": "2.0.59",
"description": "Unofficial Online Realtime D&D 5e App",
"license": "GPL-3.0",
"repository": {

View File

@@ -10,6 +10,7 @@
"preserveSymlinks": true,
"allowJs": true,
"checkJs": true,
"outDir": "build",
"paths": {
"/*": [
"./*"