mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-23 06:59:31 +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,
|
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");
|
||||||
|
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(
|
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
|
(v) => db[collection]._collection.indexer.indices.indexOf(v) > -1
|
||||||
)
|
)
|
||||||
).toBeTruthy();
|
).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 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);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user