diff --git a/src/becca/becca.js b/src/becca/becca.js index 8b1a3fcb9..4b08b7706 100644 --- a/src/becca/becca.js +++ b/src/becca/becca.js @@ -208,6 +208,7 @@ class Becca { return this.etapiTokens[etapiTokenId]; } + /** @returns {AbstractBeccaEntity|null} */ getEntity(entityName, entityId) { if (!entityName || !entityId) { return null; diff --git a/src/becca/entities/abstract_becca_entity.js b/src/becca/entities/abstract_becca_entity.js index cb539ef5d..9448ebb50 100644 --- a/src/becca/entities/abstract_becca_entity.js +++ b/src/becca/entities/abstract_becca_entity.js @@ -18,31 +18,11 @@ let becca = null; class AbstractBeccaEntity { /** @protected */ beforeSaving() { - this.generateIdIfNecessary(); - } - - /** @protected */ - generateIdIfNecessary() { if (!this[this.constructor.primaryKeyName]) { this[this.constructor.primaryKeyName] = utils.newEntityId(); } } - /** @protected */ - generateHash(isDeleted = false) { - let contentToHash = ""; - - for (const propertyName of this.constructor.hashedProperties) { - contentToHash += `|${this[propertyName]}`; - } - - if (isDeleted) { - contentToHash += "|deleted"; - } - - return utils.hash(contentToHash).substr(0, 10); - } - /** @protected */ getUtcDateChanged() { return this.utcDateModified || this.utcDateCreated; @@ -61,7 +41,7 @@ class AbstractBeccaEntity { } /** @protected */ - putEntityChange(isDeleted = false) { + putEntityChange(isDeleted) { entityChangesService.putEntityChange({ entityName: this.constructor.entityName, entityId: this[this.constructor.primaryKeyName], @@ -72,11 +52,37 @@ class AbstractBeccaEntity { }); } + /** + * @protected + * @returns {string} + */ + generateHash(isDeleted) { + let contentToHash = ""; + + for (const propertyName of this.constructor.hashedProperties) { + contentToHash += `|${this[propertyName]}`; + } + + if (isDeleted) { + contentToHash += "|deleted"; + } + + return utils.hash(contentToHash).substr(0, 10); + } + /** @protected */ getPojoToSave() { return this.getPojo(); } + /** + * @protected + * @abstract + */ + getPojo() { + throw new Error(`Unimplemented getPojo() for entity '${this.constructor.name}'`) + } + /** * Saves entity - executes SQL, but doesn't commit the transaction on its own * @@ -88,9 +94,7 @@ class AbstractBeccaEntity { const isNewEntity = !this[primaryKeyName]; - if (this.beforeSaving) { - this.beforeSaving(opts); - } + this.beforeSaving(opts); const pojo = this.getPojoToSave(); @@ -101,7 +105,7 @@ class AbstractBeccaEntity { return; } - this.putEntityChange(false); + this.putEntityChange(!!this.isDeleted); if (!cls.isEntityEventsDisabled()) { const eventPayload = { diff --git a/src/becca/entities/battachment.js b/src/becca/entities/battachment.js index 7108545c7..2e1adfd0a 100644 --- a/src/becca/entities/battachment.js +++ b/src/becca/entities/battachment.js @@ -20,8 +20,7 @@ const attachmentRoleToNoteTypeMapping = { class BAttachment extends AbstractBeccaEntity { static get entityName() { return "attachments"; } static get primaryKeyName() { return "attachmentId"; } - static get hashedProperties() { return ["attachmentId", "ownerId", "role", "mime", "title", "blobId", - "utcDateScheduledForErasureSince", "utcDateModified"]; } + static get hashedProperties() { return ["attachmentId", "ownerId", "role", "mime", "title", "blobId", "utcDateScheduledForErasureSince"]; } constructor(row) { super(); diff --git a/src/services/entity_changes.js b/src/services/entity_changes.js index c76f0fcd8..f1fd2eb5f 100644 --- a/src/services/entity_changes.js +++ b/src/services/entity_changes.js @@ -148,12 +148,13 @@ function fillEntityChanges(entityName, entityPrimaryKey, condition = '') { const entity = becca.getEntity(entityName, entityId); if (entity) { - ec.hash = entity.generateHash() || "|deleted"; + ec.hash = entity.generateHash(); ec.utcDateChanged = entity.getUtcDateChanged() || dateUtils.utcNowDateTime(); ec.isSynced = entityName !== 'options' || !!entity.isSynced; } else { // entity might be null (not present in becca) when it's deleted - // FIXME: hacky, not sure if it might cause some problems + // this will produce different hash value than when entity is being deleted since then + // all normal hashed attributes are being used. Sync should recover from that, though. ec.hash = "deleted"; ec.utcDateChanged = dateUtils.utcNowDateTime(); ec.isSynced = true; // deletable (the ones with isDeleted) entities are synced diff --git a/src/services/import/zip.js b/src/services/import/zip.js index 15b50cd80..010a10346 100644 --- a/src/services/import/zip.js +++ b/src/services/import/zip.js @@ -456,7 +456,7 @@ async function importZip(taskContext, fileBuffer, importRootNote) { position: attachmentMeta.position }); - attachment.setContent(content); + attachment.setContent(content, { forceSave: true }); return; } diff --git a/src/services/sync_update.js b/src/services/sync_update.js index aceb0d5be..919f3c8ce 100644 --- a/src/services/sync_update.js +++ b/src/services/sync_update.js @@ -106,7 +106,7 @@ function updateNormalEntity(remoteEC, remoteEntityRow, instanceId, updateContext updateContext.updated[remoteEC.entityName] = updateContext.updated[remoteEC.entityName] || []; updateContext.updated[remoteEC.entityName].push(remoteEC.entityId); - if (!localEC || localEC.utcDateChanged < remoteEC.utcDateChanged) { + if (!localEC || localEC.utcDateChanged < remoteEC.utcDateChanged || localEC.hash !== remoteEC.hash) { entityChangesService.putEntityChangeWithInstanceId(remoteEC, instanceId); }