moving out note revision content into separate table, refactoring, WIP

This commit is contained in:
zadam
2019-10-31 21:58:34 +01:00
parent 1a182d1b58
commit cf53cbf1dd
10 changed files with 172 additions and 143 deletions

View File

@@ -3,6 +3,9 @@
const Entity = require('./entity');
const protectedSessionService = require('../services/protected_session');
const repository = require('../services/repository');
const utils = require('../services/utils');
const dateUtils = require('../services/date_utils');
const syncTableService = require('../services/sync_table');
/**
* NoteRevision represents snapshot of note's title and content at some point in the past. It's used for seamless note versioning.
@@ -12,7 +15,6 @@ const repository = require('../services/repository');
* @param {string} type
* @param {string} mime
* @param {string} title
* @param {string} content
* @param {string} isProtected
* @param {string} dateModifiedFrom
* @param {string} dateModifiedTo
@@ -24,7 +26,7 @@ const repository = require('../services/repository');
class NoteRevision extends Entity {
static get entityName() { return "note_revisions"; }
static get primaryKeyName() { return "noteRevisionId"; }
static get hashedProperties() { return ["noteRevisionId", "noteId", "title", "content", "isProtected", "dateModifiedFrom", "dateModifiedTo", "utcDateModifiedFrom", "utcDateModifiedTo"]; }
static get hashedProperties() { return ["noteRevisionId", "noteId", "title", "isProtected", "dateModifiedFrom", "dateModifiedTo", "utcDateModifiedFrom", "utcDateModifiedTo"]; }
constructor(row) {
super(row);
@@ -32,7 +34,12 @@ class NoteRevision extends Entity {
this.isProtected = !!this.isProtected;
if (this.isProtected) {
protectedSessionService.decryptNoteRevision(this);
if (protectedSessionService.isProtectedSessionAvailable()) {
this.title = protectedSessionService.decrypt(this.title);
}
else {
this.title = "[Protected]";
}
}
}
@@ -40,12 +47,97 @@ class NoteRevision extends Entity {
return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]);
}
beforeSaving() {
if (this.isProtected) {
protectedSessionService.encryptNoteRevision(this);
/** @returns {boolean} true if the note has string content (not binary) */
isStringNote() {
return utils.isStringNote(this.type, this.mime);
}
/*
* Note revision content has quite special handling - it's not a separate entity, but a lazily loaded
* part of NoteRevision entity with it's own sync. Reason behind this hybrid design is that
* content can be quite large and it's not necessary to load it / fill memory for any note access even
* if we don't need a content, especially for bulk operations like search.
*
* This is the same approach as is used for Note's content.
*/
/** @returns {Promise<*>} */
async getContent(silentNotFoundError = false) {
if (this.content === undefined) {
const res = await sql.getRow(`SELECT content, hash FROM note_revision_contents WHERE noteRevisionId = ?`, [this.noteRevisionId]);
if (!res) {
if (silentNotFoundError) {
return undefined;
}
else {
throw new Error("Cannot find note revision content for noteRevisionId=" + this.noteRevisionId);
}
}
this.content = res.content;
if (this.isProtected) {
if (protectedSessionService.isProtectedSessionAvailable()) {
this.content = protectedSessionService.decrypt(this.content);
}
else {
this.content = "";
}
}
if (this.isStringNote()) {
this.content = this.content === null
? ""
: this.content.toString("UTF-8");
}
}
super.beforeSaving();
return this.content;
}
/** @returns {Promise} */
async setContent(content) {
// force updating note itself so that dateChanged is represented correctly even for the content
this.forcedChange = true;
await this.save();
this.content = content;
const pojo = {
noteRevisionId: this.noteRevisionId,
content: content,
utcDateModified: dateUtils.utcNowDateTime(),
hash: utils.hash(this.noteRevisionId + "|" + content)
};
if (this.isProtected) {
if (protectedSessionService.isProtectedSessionAvailable()) {
pojo.content = protectedSessionService.encrypt(pojo.content);
}
else {
throw new Error(`Cannot update content of noteRevisionId=${this.noteRevisionId} since we're out of protected session.`);
}
}
await sql.upsert("note_revision_contents", "noteRevisionId", pojo);
await syncTableService.addNoteContentSync(this.noteId);
}
// cannot be static!
updatePojo(pojo) {
if (pojo.isProtected) {
if (protectedSessionService.isProtectedSessionAvailable()) {
pojo.title = protectedSessionService.encrypt(pojo.title);
}
else {
// updating protected note outside of protected session means we will keep original ciphertexts
delete pojo.title;
}
}
delete pojo.content;
}
}