mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-23 15:09:33 +01:00
feat: add backup v2 migration
This commit is contained in:
1
packages/core/__tests__/__fixtures__/backup.v2.json
Normal file
1
packages/core/__tests__/__fixtures__/backup.v2.json
Normal file
File diff suppressed because one or more lines are too long
@@ -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");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user