feat: add backup v2 migration

This commit is contained in:
thecodrr
2020-12-05 13:59:27 +05:00
parent 912cb6ff22
commit a2f50c7e7e
3 changed files with 84 additions and 35 deletions

File diff suppressed because one or more lines are too long

View File

@@ -5,6 +5,7 @@ import {
notebookTest, notebookTest,
} from "./utils"; } from "./utils";
import v0Backup from "./__fixtures__/backup.v0.json"; import v0Backup from "./__fixtures__/backup.v0.json";
import v2Backup from "./__fixtures__/backup.v2.json";
beforeEach(async () => { beforeEach(async () => {
StorageInterface.clear(); StorageInterface.clear();
@@ -70,7 +71,11 @@ test("import unversioned (v0) backup", () => {
expect( expect(
db.notes.all.every( db.notes.all.every(
(v) => v.contentId && !v.content && (!v.notebook || !v.notebook.id) (v) =>
v.contentId &&
!v.content &&
!v.notebook &&
(!v.notebooks || Array.isArray(v.notebooks))
) )
).toBeTruthy(); ).toBeTruthy();
@@ -78,20 +83,43 @@ test("import unversioned (v0) backup", () => {
db.notebooks.all.every((v) => v.title != null && v.description != null) db.notebooks.all.every((v) => v.title != null && v.description != null)
).toBeTruthy(); ).toBeTruthy();
function verifyIndex(db, backupCollection, collection) { verifyIndex(v0Backup, db, "notes", "notes");
if (!v0Backup.data[backupCollection]) return; verifyIndex(v0Backup, db, "notebooks", "notebooks");
expect( verifyIndex(v0Backup, db, "delta", "content");
v0Backup.data[backupCollection].every( verifyIndex(v0Backup, db, "tags", "tags");
(v) => db[collection]._collection.indexer.indices.indexOf(v) > -1 verifyIndex(v0Backup, db, "colors", "colors");
) verifyIndex(v0Backup, db, "trash", "trash");
).toBeTruthy();
}
verifyIndex(db, "notes", "notes");
verifyIndex(db, "notebooks", "notebooks");
verifyIndex(db, "delta", "content");
verifyIndex(db, "tags", "tags");
verifyIndex(db, "colors", "colors");
verifyIndex(db, "trash", "trash");
}); });
}); });
test("import v2 backup", () => {
return databaseTest().then(async (db) => {
await db.backup.import(JSON.stringify(v2Backup));
expect(db.settings.raw.id).toBeDefined();
expect(db.settings.raw.pins.length).toBeGreaterThan(0);
expect(
db.notes.all.every(
(v) => !v.notebook && (!v.notebooks || Array.isArray(v.notebooks))
)
).toBeTruthy();
verifyIndex(v2Backup, db, "notes", "notes");
verifyIndex(v2Backup, db, "notebooks", "notebooks");
verifyIndex(v2Backup, db, "content", "content");
verifyIndex(v2Backup, db, "tags", "tags");
verifyIndex(v2Backup, db, "colors", "colors");
verifyIndex(v2Backup, db, "trash", "trash");
});
});
function verifyIndex(backup, db, backupCollection, collection) {
if (!backup.data[backupCollection]) return;
expect(
backup.data[backupCollection].every(
(v) => db[collection]._collection.indexer.indices.indexOf(v) > -1
)
).toBeTruthy();
}

View File

@@ -1,10 +1,10 @@
import Hashes from "jshashes"; import Hashes from "jshashes";
import { sendCheckUserStatusEvent } from "../common.js"; import { CHECK_IDS, sendCheckUserStatusEvent } from "../common.js";
const md5 = new Hashes.MD5(); const md5 = new Hashes.MD5();
const invalidKeys = ["user", "t", "lastBackupTime"]; const invalidKeys = ["user", "t", "lastBackupTime"];
const validTypes = ["mobile", "web", "node"]; const validTypes = ["mobile", "web", "node"];
const CURRENT_BACKUP_VERSION = 2; const CURRENT_BACKUP_VERSION = 3;
export default class Backup { export default class Backup {
/** /**
* *
@@ -24,7 +24,8 @@ export default class Backup {
* @param {boolean} encrypt * @param {boolean} encrypt
*/ */
async export(type, encrypt = false) { async export(type, encrypt = false) {
if (encrypt && !(await sendCheckUserStatusEvent("backup:encrypt"))) return; if (encrypt && !(await sendCheckUserStatusEvent(CHECK_IDS.backupEncrypt)))
return;
if (!validTypes.some((t) => t === type)) if (!validTypes.some((t) => t === type))
throw new Error("Invalid type. It must be one of 'mobile' or 'web'."); throw new Error("Invalid type. It must be one of 'mobile' or 'web'.");
@@ -87,7 +88,8 @@ export default class Backup {
); );
switch (version) { switch (version) {
case CURRENT_BACKUP_VERSION: { case CURRENT_BACKUP_VERSION:
case 2: {
return backup; return backup;
} }
case 0: { case 0: {
@@ -125,17 +127,31 @@ export default class Backup {
"delta", "delta",
"text", "text",
"content", "content",
"settings",
]; ];
await Promise.all( await Promise.all(
collections.map(async (collection) => { collections.map(async (collection) => {
const collectionIndex = data[collection]; const collectionIndex = data[collection];
if (!collectionIndex) return; if (!collectionIndex) return;
if (!Array.isArray(collectionIndex)) {
let migrationFunction = migrations[version][collection];
if (!migrationFunction)
migrationFunction = migrations[CURRENT_BACKUP_VERSION][collection];
await migrationFunction(this._db, collectionIndex);
return;
}
await Promise.all( await Promise.all(
collectionIndex.map(async (id) => { collectionIndex.map(async (id) => {
const item = data[id]; const item = data[id];
if (!item) return; if (!item) return;
await migrations[version][collection](this._db, item); let migrationFunction = migrations[version][collection];
if (!migrationFunction)
migrationFunction =
migrations[CURRENT_BACKUP_VERSION][collection];
await migrationFunction(this._db, item);
}) })
); );
}) })
@@ -203,21 +219,22 @@ const migrations = {
} }
await db.trash.add(item); await db.trash.add(item);
}, },
notebooks: async function (db, item) {
if (await migrations.handleDeleted(db, "notebooks", item)) return;
await db.notebooks.add(item);
},
tags: async function (db, item) {
if (await migrations.handleDeleted(db, "tags", item)) return;
await db.tags.add(item);
},
colors: async function (db, item) {
if (await migrations.handleDeleted(db, "colors", item)) return;
await db.tags.add(item);
},
text: function () {}, text: function () {},
}, },
2: { 2: {
notes: async function (db, item) {
if (await migrations.handleDeleted(db, "notes", item)) return;
// notebook -> notebooks
const notebook = item.notebook;
delete item.notebook;
item.remote = true;
if (notebook) item.notebooks = [notebook];
await db.notes.add({ ...item, remote: true });
},
},
3: {
notes: async function (db, item) { notes: async function (db, item) {
if (await migrations.handleDeleted(db, "notes", item)) return; if (await migrations.handleDeleted(db, "notes", item)) return;
await db.notes.add({ ...item, remote: true }); await db.notes.add({ ...item, remote: true });
@@ -228,11 +245,11 @@ const migrations = {
}, },
tags: async function (db, item) { tags: async function (db, item) {
if (await migrations.handleDeleted(db, "tags", item)) return; if (await migrations.handleDeleted(db, "tags", item)) return;
await db.tags.add(item); await db.tags.merge(item);
}, },
colors: async function (db, item) { colors: async function (db, item) {
if (await migrations.handleDeleted(db, "colors", item)) return; if (await migrations.handleDeleted(db, "colors", item)) return;
await db.tags.add(item); await db.colors.merge(item);
}, },
trash: async function (db, item) { trash: async function (db, item) {
if (await migrations.handleDeleted(db, "trash", item)) return; if (await migrations.handleDeleted(db, "trash", item)) return;
@@ -242,5 +259,8 @@ const migrations = {
if (await migrations.handleDeleted(db, "content", item)) return; if (await migrations.handleDeleted(db, "content", item)) return;
await db.content.add(item); await db.content.add(item);
}, },
settings: async function (db, item) {
db.settings.merge(item);
},
}, },
}; };