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,
} from "./utils";
import v0Backup from "./__fixtures__/backup.v0.json";
import v2Backup from "./__fixtures__/backup.v2.json";
beforeEach(async () => {
StorageInterface.clear();
@@ -70,7 +71,11 @@ test("import unversioned (v0) backup", () => {
expect(
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();
@@ -78,20 +83,43 @@ test("import unversioned (v0) backup", () => {
db.notebooks.all.every((v) => v.title != null && v.description != null)
).toBeTruthy();
function verifyIndex(db, backupCollection, collection) {
if (!v0Backup.data[backupCollection]) return;
verifyIndex(v0Backup, db, "notes", "notes");
verifyIndex(v0Backup, db, "notebooks", "notebooks");
verifyIndex(v0Backup, db, "delta", "content");
verifyIndex(v0Backup, db, "tags", "tags");
verifyIndex(v0Backup, db, "colors", "colors");
verifyIndex(v0Backup, 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(
v0Backup.data[backupCollection].every(
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();
}
verifyIndex(db, "notes", "notes");
verifyIndex(db, "notebooks", "notebooks");
verifyIndex(db, "delta", "content");
verifyIndex(db, "tags", "tags");
verifyIndex(db, "colors", "colors");
verifyIndex(db, "trash", "trash");
});
});

View File

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