diff --git a/src/entities/attribute.js b/src/entities/attribute.js deleted file mode 100644 index fe6a2f44f..000000000 --- a/src/entities/attribute.js +++ /dev/null @@ -1,124 +0,0 @@ -"use strict"; - - -const Entity = require('./entity'); -const dateUtils = require('../services/date_utils'); -const sql = require('../services/sql'); -const promotedAttributeDefinitionParser = require("../services/promoted_attribute_definition_parser"); - -/** - * Attribute is key value pair owned by a note. - * - * @property {string} attributeId - immutable - * @property {string} noteId - immutable - * @property {string} type - immutable - * @property {string} name - immutable - * @property {string} value - * @property {int} position - * @property {boolean} isInheritable - immutable - * @property {boolean} isDeleted - * @property {string|null} deleteId - ID identifying delete transaction - * @property {string} utcDateModified - * - * @extends Entity - */ -class Attribute extends Entity { - static get entityName() { return "attributes"; } - static get primaryKeyName() { return "attributeId"; } - static get hashedProperties() { return ["attributeId", "noteId", "type", "name", "value", "isInheritable", "isDeleted"]; } - - constructor(row) { - super(row); - - this.isInheritable = !!this.isInheritable; - } - - isAutoLink() { - return this.type === 'relation' && ['internalLink', 'imageLink', 'relationMapLink', 'includeNoteLink'].includes(this.name); - } - - /** - * @returns {Note|null} - */ - getNote() { - return this.becca.getNote(this.noteId); - } - - /** - * @returns {Note|null} - */ - getTargetNote() { - if (this.type !== 'relation') { - throw new Error(`Attribute ${this.attributeId} is not relation`); - } - - if (!this.value) { - return null; - } - - return this.becca.getNote(this.value); - } - - /** - * @return {boolean} - */ - isDefinition() { - return this.type === 'label' && (this.name.startsWith('label:') || this.name.startsWith('relation:')); - } - - getDefinition() { - return promotedAttributeDefinitionParser.parse(this.value); - } - - getDefinedName() { - if (this.type === 'label' && this.name.startsWith('label:')) { - return this.name.substr(6); - } else if (this.type === 'label' && this.name.startsWith('relation:')) { - return this.name.substr(9); - } else { - return this.name; - } - } - - beforeSaving() { - if (!this.value) { - if (this.type === 'relation') { - throw new Error(`Cannot save relation ${this.name} since it does not target any note.`); - } - - // null value isn't allowed - this.value = ""; - } - - if (this.position === undefined) { - this.position = 1 + sql.getValue(`SELECT COALESCE(MAX(position), 0) FROM attributes WHERE noteId = ?`, [this.noteId]); - } - - if (!this.isInheritable) { - this.isInheritable = false; - } - - if (!this.isDeleted) { - this.isDeleted = false; - } - - super.beforeSaving(); - - this.utcDateModified = dateUtils.utcNowDateTime(); - } - - createClone(type, name, value, isInheritable) { - return new Attribute({ - noteId: this.noteId, - type: type, - name: name, - value: value, - position: this.position, - isInheritable: isInheritable, - isDeleted: false, - utcDateModified: this.utcDateModified - }); - } -} - -module.exports = Attribute; diff --git a/src/entities/branch.js b/src/entities/branch.js deleted file mode 100644 index 61d48e32c..000000000 --- a/src/entities/branch.js +++ /dev/null @@ -1,77 +0,0 @@ -"use strict"; - -const Entity = require('./entity'); -const dateUtils = require('../services/date_utils'); -const sql = require('../services/sql'); - -/** - * Branch represents note's placement in the tree - it's essentially pair of noteId and parentNoteId. - * Each note can have multiple (at least one) branches, meaning it can be placed into multiple places in the tree. - * - * @property {string} branchId - primary key, immutable - * @property {string} noteId - immutable - * @property {string} parentNoteId - immutable - * @property {int} notePosition - * @property {string} prefix - * @property {boolean} isExpanded - * @property {boolean} isDeleted - * @property {string|null} deleteId - ID identifying delete transaction - * @property {string} utcDateModified - * @property {string} utcDateCreated - * - * @extends Entity - */ -class Branch extends Entity { - static get entityName() { return "branches"; } - static get primaryKeyName() { return "branchId"; } - // notePosition is not part of hash because it would produce a lot of updates in case of reordering - static get hashedProperties() { return ["branchId", "noteId", "parentNoteId", "isDeleted", "deleteId", "prefix"]; } - - /** @returns {Note|null} */ - getNote() { - return this.becca.getNote(this.noteId); - } - - /** @returns {Note|null} */ - getParentNote() { - return this.becca.getNote(this.parentNoteId); - } - - beforeSaving() { - if (this.notePosition === undefined || this.notePosition === null) { - const maxNotePos = sql.getValue('SELECT MAX(notePosition) FROM branches WHERE parentNoteId = ? AND isDeleted = 0', [this.parentNoteId]); - this.notePosition = maxNotePos === null ? 0 : maxNotePos + 10; - } - - if (!this.isExpanded) { - this.isExpanded = false; - } - - if (!this.isDeleted) { - this.isDeleted = false; - } - - if (!this.utcDateCreated) { - this.utcDateCreated = dateUtils.utcNowDateTime(); - } - - super.beforeSaving(); - - this.utcDateModified = dateUtils.utcNowDateTime(); - } - - createClone(parentNoteId, notePosition) { - return new Branch({ - noteId: this.noteId, - parentNoteId: parentNoteId, - notePosition: notePosition, - prefix: this.prefix, - isExpanded: this.isExpanded, - isDeleted: false, - utcDateCreated: this.utcDateCreated, - utcDateModified: this.utcDateModified - }); - } -} - -module.exports = Branch; diff --git a/src/entities/entity.js b/src/entities/entity.js deleted file mode 100644 index 1bb652583..000000000 --- a/src/entities/entity.js +++ /dev/null @@ -1,62 +0,0 @@ -"use strict"; - -const utils = require('../services/utils'); -let repo = null; - -class Entity { - /** - * @param {object} [row] - database row representing given entity - */ - constructor(row = {}) { - for (const key in row) { - // ! is used when joint-fetching notes and note_contents objects for performance - if (!key.startsWith('!')) { - this[key] = row[key]; - } - } - - if ('isDeleted' in this && this.constructor.entityName !== 'recent_notes') { - this.isDeleted = !!this.isDeleted; - } - } - - beforeSaving() { - this.generateIdIfNecessary(); - } - - generateIdIfNecessary() { - if (!this[this.constructor.primaryKeyName]) { - this[this.constructor.primaryKeyName] = utils.newEntityId(); - } - } - - generateHash() { - let contentToHash = ""; - - for (const propertyName of this.constructor.hashedProperties) { - contentToHash += "|" + this[propertyName]; - } - - return utils.hash(contentToHash).substr(0, 10); - } - - getUtcDateChanged() { - return this.utcDateModified || this.utcDateCreated; - } - - get repository() { - if (!repo) { - repo = require('../services/repository'); - } - - return repo; - } - - save() { - this.repository.updateEntity(this); - - return this; - } -} - -module.exports = Entity; diff --git a/src/entities/note.js b/src/entities/note.js deleted file mode 100644 index f24f67bf5..000000000 --- a/src/entities/note.js +++ /dev/null @@ -1,957 +0,0 @@ -"use strict"; - -const Entity = require('./entity'); -const Attribute = require('./attribute'); -const protectedSessionService = require('../services/protected_session'); -const sql = require('../services/sql'); -const utils = require('../services/utils'); -const dateUtils = require('../services/date_utils'); -const entityChangesService = require('../services/entity_changes.js'); - -const LABEL = 'label'; -const RELATION = 'relation'; - -/** - * This represents a Note which is a central object in the Trilium Notes project. - * - * @property {string} noteId - primary key - * @property {string} type - one of "text", "code", "file" or "render" - * @property {string} mime - MIME type, e.g. "text/html" - * @property {string} title - note title - * @property {boolean} isProtected - true if note is protected - * @property {boolean} isDeleted - true if note is deleted - * @property {string|null} deleteId - ID identifying delete transaction - * @property {string} dateCreated - local date time (with offset) - * @property {string} dateModified - local date time (with offset) - * @property {string} utcDateCreated - * @property {string} utcDateModified - * - * @extends Entity - */ -class Note extends Entity { - static get entityName() { return "notes"; } - static get primaryKeyName() { return "noteId"; } - static get hashedProperties() { return ["noteId", "title", "type", "mime", "isProtected", "isDeleted", "deleteId"]; } - - /** - * @param row - object containing database row from "notes" table - */ - constructor(row) { - super(row); - - this.isProtected = !!this.isProtected; - /* true if content is either not encrypted - * or encrypted, but with available protected session (so effectively decrypted) */ - this.isContentAvailable = true; - - // check if there's noteId, otherwise this is a new entity which wasn't encrypted yet - if (this.isProtected && this.noteId) { - this.isContentAvailable = protectedSessionService.isProtectedSessionAvailable(); - - if (this.isContentAvailable) { - try { - this.title = protectedSessionService.decryptString(this.title); - } - catch (e) { - throw new Error(`Could not decrypt title of note ${this.noteId}: ${e.message} ${e.stack}`) - } - } - else { - this.title = "[protected]"; - } - } - } - - /* - * Note content has quite special handling - it's not a separate entity, but a lazily loaded - * part of Note entity with it's own sync. Reasons behind this hybrid design has been: - * - * - 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 - * - changes in the note metadata or title should not trigger note content sync (so we keep separate utcDateModified and entity changes records) - * - but to the user note content and title changes are one and the same - single dateModified (so all changes must go through Note and content is not a separate entity) - */ - - /** @returns {*} */ - getContent(silentNotFoundError = false) { - if (this.content === undefined) { - const row = sql.getRow(`SELECT content FROM note_contents WHERE noteId = ?`, [this.noteId]); - - if (!row) { - if (silentNotFoundError) { - return undefined; - } - else { - throw new Error("Cannot find note content for noteId=" + this.noteId); - } - } - - this.content = row.content; - - if (this.isProtected) { - if (this.isContentAvailable) { - this.content = this.content === null ? null : protectedSessionService.decrypt(this.content); - } - else { - this.content = ""; - } - } - } - - if (this.isStringNote()) { - return this.content === null - ? "" - : this.content.toString("UTF-8"); - } - else { - return this.content; - } - } - - /** @returns {{contentLength, dateModified, utcDateModified}} */ - getContentMetadata() { - return sql.getRow(` - SELECT - LENGTH(content) AS contentLength, - dateModified, - utcDateModified - FROM note_contents - WHERE noteId = ?`, [this.noteId]); - } - - /** @returns {*} */ - getJsonContent() { - const content = this.getContent(); - - if (!content || !content.trim()) { - return null; - } - - return JSON.parse(content); - } - - setContent(content) { - if (content === null || content === undefined) { - throw new Error(`Cannot set null content to note ${this.noteId}`); - } - - if (this.isStringNote()) { - content = content.toString(); - } - else { - content = Buffer.isBuffer(content) ? content : Buffer.from(content); - } - - this.content = content; - - const pojo = { - noteId: this.noteId, - content: content, - dateModified: dateUtils.localNowDateTime(), - utcDateModified: dateUtils.utcNowDateTime() - }; - - if (this.isProtected) { - if (this.isContentAvailable) { - pojo.content = protectedSessionService.encrypt(pojo.content); - } - else { - throw new Error(`Cannot update content of noteId=${this.noteId} since we're out of protected session.`); - } - } - - sql.upsert("note_contents", "noteId", pojo); - - const hash = utils.hash(this.noteId + "|" + pojo.content.toString()); - - entityChangesService.addEntityChange({ - entityName: 'note_contents', - entityId: this.noteId, - hash: hash, - isErased: false, - utcDateChanged: pojo.utcDateModified - }, null); - } - - setJsonContent(content) { - this.setContent(JSON.stringify(content, null, '\t')); - } - - /** @returns {boolean} true if this note is the root of the note tree. Root note has "root" noteId */ - isRoot() { - return this.noteId === 'root'; - } - - /** @returns {boolean} true if this note is of application/json content type */ - isJson() { - return this.mime === "application/json"; - } - - /** @returns {boolean} true if this note is JavaScript (code or attachment) */ - isJavaScript() { - return (this.type === "code" || this.type === "file") - && (this.mime.startsWith("application/javascript") - || this.mime === "application/x-javascript" - || this.mime === "text/javascript"); - } - - /** @returns {boolean} true if this note is HTML */ - isHtml() { - return (this.type === "code" || this.type === "file" || this.type === "render") && this.mime === "text/html"; - } - - /** @returns {boolean} true if the note has string content (not binary) */ - isStringNote() { - return utils.isStringNote(this.type, this.mime); - } - - /** @returns {string} JS script environment - either "frontend" or "backend" */ - getScriptEnv() { - if (this.isHtml() || (this.isJavaScript() && this.mime.endsWith('env=frontend'))) { - return "frontend"; - } - - if (this.type === 'render') { - return "frontend"; - } - - if (this.isJavaScript() && this.mime.endsWith('env=backend')) { - return "backend"; - } - - return null; - } - - loadOwnedAttributesToCache() { - this.__ownedAttributeCache = this.repository.getEntities(`SELECT * FROM attributes WHERE isDeleted = 0 AND noteId = ?`, [this.noteId]); - return this.__ownedAttributeCache; - } - - /** - * This method is a faster variant of getAttributes() which looks for only owned attributes. - * Use when inheritance is not needed and/or in batch/performance sensitive operations. - * - * @param {string} [type] - (optional) attribute type to filter - * @param {string} [name] - (optional) attribute name to filter - * @returns {Attribute[]} note's "owned" attributes - excluding inherited ones - */ - getOwnedAttributes(type, name) { - if (!this.__ownedAttributeCache) { - this.loadOwnedAttributesToCache(); - } - - if (type && name) { - return this.__ownedAttributeCache.filter(attr => attr.type === type && attr.name === name); - } - else if (type) { - return this.__ownedAttributeCache.filter(attr => attr.type === type); - } - else if (name) { - return this.__ownedAttributeCache.filter(attr => attr.name === name); - } - else { - return this.__ownedAttributeCache.slice(); - } - } - - /** - * @returns {Attribute} attribute belonging to this specific note (excludes inherited attributes) - * - * This method can be significantly faster than the getAttribute() - */ - getOwnedAttribute(type, name) { - const attrs = this.getOwnedAttributes(type, name); - - return attrs.length > 0 ? attrs[0] : null; - } - - /** - * @returns {Attribute[]} relations targetting this specific note - */ - getTargetRelations() { - return this.repository.getEntities("SELECT * FROM attributes WHERE type = 'relation' AND isDeleted = 0 AND value = ?", [this.noteId]); - } - - /** - * @param {string} [type] - (optional) attribute type to filter - * @param {string} [name] - (optional) attribute name to filter - * @returns {Attribute[]} all note's attributes, including inherited ones - */ - getAttributes(type, name) { - if (!this.__attributeCache) { - this.loadAttributesToCache(); - } - - if (type && name) { - return this.__attributeCache.filter(attr => attr.type === type && attr.name === name); - } - else if (type) { - return this.__attributeCache.filter(attr => attr.type === type); - } - else if (name) { - return this.__attributeCache.filter(attr => attr.name === name); - } - else { - return this.__attributeCache.slice(); - } - } - - /** - * @param {string} [name] - label name to filter - * @returns {Attribute[]} all note's labels (attributes with type label), including inherited ones - */ - getLabels(name) { - return this.getAttributes(LABEL, name); - } - - /** - * @param {string} [name] - label name to filter - * @returns {string[]} all note's label values, including inherited ones - */ - getLabelValues(name) { - return this.getLabels(name).map(l => l.value); - } - - /** - * @param {string} [name] - label name to filter - * @returns {Attribute[]} all note's labels (attributes with type label), excluding inherited ones - */ - getOwnedLabels(name) { - return this.getOwnedAttributes(LABEL, name); - } - - /** - * @param {string} [name] - label name to filter - * @returns {string[]} all note's label values, excluding inherited ones - */ - getOwnedLabelValues(name) { - return this.getOwnedAttributes(LABEL, name).map(l => l.value); - } - - /** - * @param {string} [name] - relation name to filter - * @returns {Attribute[]} all note's relations (attributes with type relation), including inherited ones - */ - getRelations(name) { - return this.getAttributes(RELATION, name); - } - - /** - * @param {string} [name] - relation name to filter - * @returns {Attribute[]} all note's relations (attributes with type relation), excluding inherited ones - */ - getOwnedRelations(name) { - return this.getOwnedAttributes(RELATION, name); - } - - /** - * @param {string} [name] - relation name to filter - * @returns {Note[]} - */ - getRelationTargets(name) { - const relations = this.getRelations(name); - const targets = []; - - for (const relation of relations) { - targets.push(relation.getTargetNote()); - } - - return targets; - } - - /** - * Clear note's attributes cache to force fresh reload for next attribute request. - * Cache is note instance scoped. - */ - invalidateAttributeCache() { - this.__attributeCache = null; - this.__ownedAttributeCache = null; - } - - loadAttributesToCache() { - const attributes = this.repository.getEntities(` - WITH RECURSIVE - tree(noteId, level) AS ( - SELECT ?, 0 - UNION - SELECT branches.parentNoteId, tree.level + 1 - FROM branches - JOIN tree ON branches.noteId = tree.noteId - WHERE branches.isDeleted = 0 - ), - treeWithAttrs(noteId, level) AS ( - SELECT * FROM tree - UNION - SELECT attributes.value, treeWithAttrs.level FROM attributes - JOIN treeWithAttrs ON treeWithAttrs.noteId = attributes.noteId - WHERE attributes.isDeleted = 0 - AND attributes.type = 'relation' - AND attributes.name = 'template' - ) - SELECT attributes.* FROM attributes JOIN treeWithAttrs ON attributes.noteId = treeWithAttrs.noteId - WHERE attributes.isDeleted = 0 AND (attributes.isInheritable = 1 OR treeWithAttrs.level = 0) - ORDER BY level, noteId, position`, [this.noteId]); - // attributes are ordered so that "closest" attributes are first - // we order by noteId so that attributes from same note stay together. Actual noteId ordering doesn't matter. - - const filteredAttributes = attributes.filter((attr, index) => { - // if this exact attribute already appears then don't include it (can happen via cloning) - if (attributes.findIndex(it => it.attributeId === attr.attributeId) !== index) { - return false; - } - - // FIXME: this code is quite questionable, one problem is that other caches (Froca, Becca) have nothing like that - if (attr.isDefinition()) { - const firstDefinitionIndex = attributes.findIndex(el => el.type === attr.type && el.name === attr.name); - - // keep only if this element is the first definition for this type & name - return firstDefinitionIndex === index; - } - else { - const definitionAttr = attributes.find(el => el.type === 'label' && el.name === attr.type + ':' + attr.name); - - if (!definitionAttr) { - return true; - } - - const definition = definitionAttr.getDefinition(); - - if (definition.multiplicity === 'multi') { - return true; - } - else { - const firstAttrIndex = attributes.findIndex(el => el.type === attr.type && el.name === attr.name); - - // in case of single-valued attribute we'll keep it only if it's first (closest) - return firstAttrIndex === index; - } - } - }); - - this.__attributeCache = filteredAttributes; - } - - /** - * @param {string} type - attribute type (label, relation, etc.) - * @param {string} name - attribute name - * @returns {boolean} true if note has an attribute with given type and name (including inherited) - */ - hasAttribute(type, name) { - return !!this.getAttribute(type, name); - } - - /** - * @param {string} type - attribute type (label, relation, etc.) - * @param {string} name - attribute name - * @returns {boolean} true if note has an attribute with given type and name (excluding inherited) - */ - hasOwnedAttribute(type, name) { - return !!this.getOwnedAttribute(type, name); - } - - /** - * @param {string} type - attribute type (label, relation, etc.) - * @param {string} name - attribute name - * @returns {Attribute} attribute of given type and name. If there's more such attributes, first is returned. Returns null if there's no such attribute belonging to this note. - */ - getAttribute(type, name) { - const attributes = this.getAttributes(); - - return attributes.find(attr => attr.type === type && attr.name === name); - } - - /** - * @param {string} type - attribute type (label, relation, etc.) - * @param {string} name - attribute name - * @returns {string|null} attribute value of given type and name or null if no such attribute exists. - */ - getAttributeValue(type, name) { - const attr = this.getAttribute(type, name); - - return attr ? attr.value : null; - } - - /** - * @param {string} type - attribute type (label, relation, etc.) - * @param {string} name - attribute name - * @returns {string|null} attribute value of given type and name or null if no such attribute exists. - */ - getOwnedAttributeValue(type, name) { - const attr = this.getOwnedAttribute(type, name); - - return attr ? attr.value : null; - } - - /** - * Based on enabled, attribute is either set or removed. - * - * @param {string} type - attribute type ('relation', 'label' etc.) - * @param {boolean} enabled - toggle On or Off - * @param {string} name - attribute name - * @param {string} [value] - attribute value (optional) - */ - toggleAttribute(type, enabled, name, value) { - if (enabled) { - this.setAttribute(type, name, value); - } - else { - this.removeAttribute(type, name, value); - } - } - - /** - * Update's given attribute's value or creates it if it doesn't exist - * - * @param {string} type - attribute type (label, relation, etc.) - * @param {string} name - attribute name - * @param {string} [value] - attribute value (optional) - */ - setAttribute(type, name, value) { - const attributes = this.loadOwnedAttributesToCache(); - let attr = attributes.find(attr => attr.type === type && attr.name === name); - - if (attr) { - if (attr.value !== value) { - attr.value = value; - attr.save(); - - this.invalidateAttributeCache(); - } - } - else { - attr = new Attribute({ - noteId: this.noteId, - type: type, - name: name, - value: value !== undefined ? value : "" - }); - - attr.save(); - - this.invalidateAttributeCache(); - } - } - - /** - * Removes given attribute name-value pair if it exists. - * - * @param {string} type - attribute type (label, relation, etc.) - * @param {string} name - attribute name - * @param {string} [value] - attribute value (optional) - */ - removeAttribute(type, name, value) { - const attributes = this.loadOwnedAttributesToCache(); - - for (const attribute of attributes) { - if (attribute.type === type && attribute.name === name && (value === undefined || value === attribute.value)) { - attribute.markAsDeleted(); - - this.invalidateAttributeCache(); - } - } - } - - /** - * @return {Attribute} - */ - addAttribute(type, name, value = "", isInheritable = false, position = 1000) { - const attr = new Attribute({ - noteId: this.noteId, - type: type, - name: name, - value: value, - isInheritable: isInheritable, - position: position - }); - - attr.save(); - - this.invalidateAttributeCache(); - - return attr; - } - - addLabel(name, value = "", isInheritable = false) { - return this.addAttribute(LABEL, name, value, isInheritable); - } - - addRelation(name, targetNoteId, isInheritable = false) { - return this.addAttribute(RELATION, name, targetNoteId, isInheritable); - } - - /** - * @param {string} name - label name - * @returns {boolean} true if label exists (including inherited) - */ - hasLabel(name) { return this.hasAttribute(LABEL, name); } - - /** - * @param {string} name - label name - * @returns {boolean} true if label exists (excluding inherited) - */ - hasOwnedLabel(name) { return this.hasOwnedAttribute(LABEL, name); } - - /** - * @param {string} name - relation name - * @returns {boolean} true if relation exists (including inherited) - */ - hasRelation(name) { return this.hasAttribute(RELATION, name); } - - /** - * @param {string} name - relation name - * @returns {boolean} true if relation exists (excluding inherited) - */ - hasOwnedRelation(name) { return this.hasOwnedAttribute(RELATION, name); } - - /** - * @param {string} name - label name - * @returns {Attribute|null} label if it exists, null otherwise - */ - getLabel(name) { return this.getAttribute(LABEL, name); } - - /** - * @param {string} name - label name - * @returns {Attribute|null} label if it exists, null otherwise - */ - getOwnedLabel(name) { return this.getOwnedAttribute(LABEL, name); } - - /** - * @param {string} name - relation name - * @returns {Attribute|null} relation if it exists, null otherwise - */ - getRelation(name) { return this.getAttribute(RELATION, name); } - - /** - * @param {string} name - relation name - * @returns {Attribute|null} relation if it exists, null otherwise - */ - getOwnedRelation(name) { return this.getOwnedAttribute(RELATION, name); } - - /** - * @param {string} name - label name - * @returns {string|null} label value if label exists, null otherwise - */ - getLabelValue(name) { return this.getAttributeValue(LABEL, name); } - - /** - * @param {string} name - label name - * @returns {string|null} label value if label exists, null otherwise - */ - getOwnedLabelValue(name) { return this.getOwnedAttributeValue(LABEL, name); } - - /** - * @param {string} name - relation name - * @returns {string|null} relation value if relation exists, null otherwise - */ - getRelationValue(name) { return this.getAttributeValue(RELATION, name); } - - /** - * @param {string} name - relation name - * @returns {string|null} relation value if relation exists, null otherwise - */ - getOwnedRelationValue(name) { return this.getOwnedAttributeValue(RELATION, name); } - - /** - * @param {string} name - * @returns {Note|null} target note of the relation or null (if target is empty or note was not found) - */ - getRelationTarget(name) { - const relation = this.getRelation(name); - - return relation ? this.becca.getNote(relation.value) : null; - } - - /** - * @param {string} name - * @returns {Note|null} target note of the relation or null (if target is empty or note was not found) - */ - getOwnedRelationTarget(name) { - const relation = this.getOwnedRelation(name); - - return relation ? this.becca.getNote(relation.value) : null; - } - - /** - * Based on enabled, label is either set or removed. - * - * @param {boolean} enabled - toggle On or Off - * @param {string} name - label name - * @param {string} [value] - label value (optional) - */ - toggleLabel(enabled, name, value) { return this.toggleAttribute(LABEL, enabled, name, value); } - - /** - * Based on enabled, relation is either set or removed. - * - * @param {boolean} enabled - toggle On or Off - * @param {string} name - relation name - * @param {string} [value] - relation value (noteId) - */ - toggleRelation(enabled, name, value) { return this.toggleAttribute(RELATION, enabled, name, value); } - - /** - * Update's given label's value or creates it if it doesn't exist - * - * @param {string} name - label name - * @param {string} [value] - label value - */ - setLabel(name, value) { return this.setAttribute(LABEL, name, value); } - - /** - * Update's given relation's value or creates it if it doesn't exist - * - * @param {string} name - relation name - * @param {string} value - relation value (noteId) - */ - setRelation(name, value) { return this.setAttribute(RELATION, name, value); } - - /** - * Remove label name-value pair, if it exists. - * - * @param {string} name - label name - * @param {string} [value] - label value - */ - removeLabel(name, value) { return this.removeAttribute(LABEL, name, value); } - - /** - * Remove relation name-value pair, if it exists. - * - * @param {string} name - relation name - * @param {string} [value] - relation value (noteId) - */ - removeRelation(name, value) { return this.removeAttribute(RELATION, name, value); } - - /** - * @return {string[]} return list of all descendant noteIds of this note. Returning just noteIds because number of notes can be huge. Includes also this note's noteId - */ - getDescendantNoteIds() { - return sql.getColumn(` - WITH RECURSIVE - tree(noteId) AS ( - SELECT ? - UNION - SELECT branches.noteId FROM branches - JOIN tree ON branches.parentNoteId = tree.noteId - JOIN notes ON notes.noteId = branches.noteId - WHERE notes.isDeleted = 0 - AND branches.isDeleted = 0 - ) - SELECT noteId FROM tree`, [this.noteId]); - } - - /** - * Finds descendant notes with given attribute name and value. Only own attributes are considered, not inherited ones - * - * @param {string} type - attribute type (label, relation, etc.) - * @param {string} name - attribute name - * @param {string} [value] - attribute value - * @returns {Note[]} - */ - getDescendantNotesWithAttribute(type, name, value) { - const params = [this.noteId, name]; - let valueCondition = ""; - - if (value !== undefined) { - params.push(value); - valueCondition = " AND attributes.value = ?"; - } - - const notes = this.repository.getEntities(` - WITH RECURSIVE - tree(noteId) AS ( - SELECT ? - UNION - SELECT branches.noteId FROM branches - JOIN tree ON branches.parentNoteId = tree.noteId - JOIN notes ON notes.noteId = branches.noteId - WHERE notes.isDeleted = 0 - AND branches.isDeleted = 0 - ) - SELECT notes.* FROM notes - JOIN tree ON tree.noteId = notes.noteId - JOIN attributes ON attributes.noteId = notes.noteId - WHERE attributes.isDeleted = 0 - AND attributes.name = ? - ${valueCondition} - ORDER BY noteId, position`, params); - - return notes; - } - - /** - * Finds descendant notes with given label name and value. Only own labels are considered, not inherited ones - * - * @param {string} name - label name - * @param {string} [value] - label value - * @returns {Note[]} - */ - getDescendantNotesWithLabel(name, value) { return this.getDescendantNotesWithAttribute(LABEL, name, value); } - - /** - * Finds descendant notes with given relation name and value. Only own relations are considered, not inherited ones - * - * @param {string} name - relation name - * @param {string} [value] - relation value - * @returns {Note[]} - */ - getDescendantNotesWithRelation(name, value) { return this.getDescendantNotesWithAttribute(RELATION, name, value); } - - /** - * Returns note revisions of this note. - * - * @returns {NoteRevision[]} - */ - getNoteRevisions() { - return this.repository.getEntities("SELECT * FROM note_revisions WHERE noteId = ?", [this.noteId]); - } - - /** - * Get list of links coming out of this note. - * - * @deprecated - not intended for general use - * @returns {Attribute[]} - */ - getLinks() { - return this.repository.getEntities(` - SELECT * - FROM attributes - WHERE noteId = ? AND - isDeleted = 0 AND - type = 'relation' AND - name IN ('internalLink', 'imageLink', 'relationMapLink', 'includeNoteLink')`, [this.noteId]); - } - - /** - * @returns {Branch[]} - */ - getBranches() { - return this.repository.getEntities("SELECT * FROM branches WHERE isDeleted = 0 AND noteId = ?", [this.noteId]); - } - - /** - * @returns {boolean} - true if note has children - */ - hasChildren() { - return this.getChildNotes().length > 0; - } - - /** - * @returns {Note[]} child notes of this note - */ - getChildNotes() { - return this.repository.getEntities(` - SELECT notes.* - FROM branches - JOIN notes USING(noteId) - WHERE notes.isDeleted = 0 - AND branches.isDeleted = 0 - AND branches.parentNoteId = ? - ORDER BY branches.notePosition`, [this.noteId]); - } - - /** - * @returns {Branch[]} child branches of this note - */ - getChildBranches() { - return this.repository.getEntities(` - SELECT branches.* - FROM branches - WHERE branches.isDeleted = 0 - AND branches.parentNoteId = ? - ORDER BY branches.notePosition`, [this.noteId]); - } - - /** - * @returns {Note[]} parent notes of this note (note can have multiple parents because of cloning) - */ - getParentNotes() { - return this.repository.getEntities(` - SELECT parent_notes.* - FROM - branches AS child_tree - JOIN notes AS parent_notes ON parent_notes.noteId = child_tree.parentNoteId - WHERE child_tree.noteId = ? - AND child_tree.isDeleted = 0 - AND parent_notes.isDeleted = 0`, [this.noteId]); - } - - /** - * @return {string[][]} - array of notePaths (each represented by array of noteIds constituting the particular note path) - */ - getAllNotePaths() { - if (this.noteId === 'root') { - return [['root']]; - } - - const notePaths = []; - - for (const parentNote of this.getParentNotes()) { - for (const parentPath of parentNote.getAllNotePaths()) { - parentPath.push(this.noteId); - notePaths.push(parentPath); - } - } - - return notePaths; - } - - getRelationDefinitions() { - return this.getLabels() - .filter(l => l.name.startsWith("relation:")); - } - - getLabelDefinitions() { - return this.getLabels() - .filter(l => l.name.startsWith("relation:")); - } - - /** - * @param ancestorNoteId - * @return {boolean} - true if ancestorNoteId occurs in at least one of the note's paths - */ - isDescendantOfNote(ancestorNoteId) { - const notePaths = this.getAllNotePaths(); - - return notePaths.some(path => path.includes(ancestorNoteId)); - } - - beforeSaving() { - if (!this.isDeleted) { - this.isDeleted = false; - } - - if (!this.dateCreated) { - this.dateCreated = dateUtils.localNowDateTime(); - } - - if (!this.utcDateCreated) { - this.utcDateCreated = dateUtils.utcNowDateTime(); - } - - super.beforeSaving(); - - this.dateModified = dateUtils.localNowDateTime(); - this.utcDateModified = dateUtils.utcNowDateTime(); - } - - // cannot be static! - updatePojo(pojo) { - if (pojo.isProtected) { - if (this.isContentAvailable) { - 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.isContentAvailable; - delete pojo.__attributeCache; - delete pojo.__ownedAttributeCache; - delete pojo.content; - /** zero references to contentHash, probably can be removed */ - delete pojo.contentHash; - } -} - -module.exports = Note; diff --git a/src/routes/api/attributes.js b/src/routes/api/attributes.js index 2794cb26a..0c3f42b0e 100644 --- a/src/routes/api/attributes.js +++ b/src/routes/api/attributes.js @@ -3,7 +3,6 @@ const sql = require('../../services/sql'); const log = require('../../services/log'); const attributeService = require('../../services/attributes'); -const repository = require('../../services/repository'); const Attribute = require('../../services/becca/entities/attribute'); const becca = require("../../services/becca/becca"); @@ -73,16 +72,17 @@ function setNoteAttribute(req) { const noteId = req.params.noteId; const body = req.body; - let attr = repository.getEntity(`SELECT * FROM attributes WHERE isDeleted = 0 AND noteId = ? AND type = ? AND name = ?`, [noteId, body.type, body.name]); + const attributeId = sql.getValue(`SELECT attributeId FROM attributes WHERE isDeleted = 0 AND noteId = ? AND type = ? AND name = ?`, [noteId, body.type, body.name]); - if (attr) { + if (attributeId) { + const attr = becca.getAttribute(attributeId); attr.value = body.value; + attr.save(); } else { - attr = new Attribute(body); + const attr = new Attribute(body); attr.noteId = noteId; + attr.save(); } - - attr.save(); } function addNoteAttribute(req) { @@ -195,16 +195,16 @@ function createRelation(req) { const targetNoteId = req.params.targetNoteId; const name = req.params.name; - let attribute = repository.getEntity(`SELECT * FROM attributes WHERE isDeleted = 0 AND noteId = ? AND type = 'relation' AND name = ? AND value = ?`, [sourceNoteId, name, targetNoteId]); + const attributeId = sql.getValue(`SELECT attributeId FROM attributes WHERE isDeleted = 0 AND noteId = ? AND type = 'relation' AND name = ? AND value = ?`, [sourceNoteId, name, targetNoteId]); + let attribute = becca.getAttribute(attributeId); if (!attribute) { - attribute = new Attribute(); - attribute.noteId = sourceNoteId; - attribute.name = name; - attribute.type = 'relation'; - attribute.value = targetNoteId; - - attribute.save(); + attribute = new Attribute({ + noteId: sourceNoteId, + name: name, + type: 'relation', + value: targetNoteId + }).save(); } return attribute; @@ -215,9 +215,10 @@ function deleteRelation(req) { const targetNoteId = req.params.targetNoteId; const name = req.params.name; - let attribute = repository.getEntity(`SELECT * FROM attributes WHERE isDeleted = 0 AND noteId = ? AND type = 'relation' AND name = ? AND value = ?`, [sourceNoteId, name, targetNoteId]); + const attributeId = sql.getValue(`SELECT attributeId FROM attributes WHERE isDeleted = 0 AND noteId = ? AND type = 'relation' AND name = ? AND value = ?`, [sourceNoteId, name, targetNoteId]); - if (attribute) { + if (attributeId) { + const attribute = becca.getAttribute(attributeId); attribute.markAsDeleted(); } } diff --git a/src/routes/api/autocomplete.js b/src/routes/api/autocomplete.js index 0b2fca7d3..9082b3d25 100644 --- a/src/routes/api/autocomplete.js +++ b/src/routes/api/autocomplete.js @@ -2,7 +2,6 @@ const beccaService = require('../../services/becca/becca_service.js'); const searchService = require('../../services/search/services/search.js'); -const repository = require('../../services/repository'); const log = require('../../services/log'); const utils = require('../../services/utils'); const cls = require('../../services/cls'); diff --git a/src/routes/api/branches.js b/src/routes/api/branches.js index 823572246..53eb027af 100644 --- a/src/routes/api/branches.js +++ b/src/routes/api/branches.js @@ -6,7 +6,6 @@ const entityChangesService = require('../../services/entity_changes.js'); const treeService = require('../../services/tree'); const noteService = require('../../services/notes'); const becca = require('../../services/becca/becca.js'); -const repository = require('../../services/repository'); const TaskContext = require('../../services/task_context'); /** diff --git a/src/routes/api/export.js b/src/routes/api/export.js index e4c81db52..5503c7a27 100644 --- a/src/routes/api/export.js +++ b/src/routes/api/export.js @@ -3,7 +3,6 @@ const zipExportService = require('../../services/export/zip'); const singleExportService = require('../../services/export/single'); const opmlExportService = require('../../services/export/opml'); -const repository = require("../../services/repository"); const TaskContext = require("../../services/task_context"); const log = require("../../services/log"); diff --git a/src/routes/api/import.js b/src/routes/api/import.js index 619ef4f07..8c8794b67 100644 --- a/src/routes/api/import.js +++ b/src/routes/api/import.js @@ -1,6 +1,5 @@ "use strict"; -const repository = require('../../services/repository'); const enexImportService = require('../../services/import/enex'); const opmlImportService = require('../../services/import/opml'); const zipImportService = require('../../services/import/zip'); diff --git a/src/routes/api/note_revisions.js b/src/routes/api/note_revisions.js index a0cf7fbab..0a1f736e8 100644 --- a/src/routes/api/note_revisions.js +++ b/src/routes/api/note_revisions.js @@ -1,6 +1,5 @@ "use strict"; -const repository = require('../../services/repository'); const beccaService = require('../../services/becca/becca_service.js'); const protectedSessionService = require('../../services/protected_session'); const noteRevisionService = require('../../services/note_revisions'); diff --git a/src/routes/api/notes.js b/src/routes/api/notes.js index ce2a2dfc5..1ee5d695a 100644 --- a/src/routes/api/notes.js +++ b/src/routes/api/notes.js @@ -2,7 +2,6 @@ const noteService = require('../../services/notes'); const treeService = require('../../services/tree'); -const repository = require('../../services/repository'); const sql = require('../../services/sql'); const utils = require('../../services/utils'); const log = require('../../services/log'); diff --git a/src/routes/api/sql.js b/src/routes/api/sql.js index a50f5829b..585beb022 100644 --- a/src/routes/api/sql.js +++ b/src/routes/api/sql.js @@ -1,7 +1,6 @@ "use strict"; const sql = require('../../services/sql'); -const repository = require('../../services/repository'); function getSchema() { const tableNames = sql.getColumn(`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name`); diff --git a/src/routes/api/sync.js b/src/routes/api/sync.js index b2aa33412..9279a6438 100644 --- a/src/routes/api/sync.js +++ b/src/routes/api/sync.js @@ -10,7 +10,6 @@ const contentHashService = require('../../services/content_hash'); const log = require('../../services/log'); const syncOptions = require('../../services/sync_options'); const dateUtils = require('../../services/date_utils'); -const entityConstructor = require('../../entities/entity_constructor'); const utils = require('../../services/utils'); async function testSync() { diff --git a/src/routes/custom.js b/src/routes/custom.js index 50bd75a63..2cc9b8d35 100644 --- a/src/routes/custom.js +++ b/src/routes/custom.js @@ -1,4 +1,3 @@ -const repository = require('../services/repository'); const log = require('../services/log'); const fileUploadService = require('./api/files.js'); const scriptService = require('../services/script'); diff --git a/src/services/backend_script_api.js b/src/services/backend_script_api.js index 660bf7e16..aaef0741b 100644 --- a/src/services/backend_script_api.js +++ b/src/services/backend_script_api.js @@ -6,7 +6,6 @@ const attributeService = require('./attributes'); const dateNoteService = require('./date_notes'); const treeService = require('./tree'); const config = require('./config'); -const repository = require('./repository'); const axios = require('axios'); const dayjs = require('dayjs'); const xml2js = require('xml2js'); diff --git a/src/services/becca/becca.js b/src/services/becca/becca.js index 1036c184f..2c03aebb5 100644 --- a/src/services/becca/becca.js +++ b/src/services/becca/becca.js @@ -93,7 +93,7 @@ class Becca { return this.options[name]; } - getEntityFromName(entityName, entityId) { + getEntity(entityName, entityId) { if (!entityName || !entityId) { return null; } diff --git a/src/entities/entity_constructor.js b/src/services/becca/entity_constructor.js similarity index 62% rename from src/entities/entity_constructor.js rename to src/services/becca/entity_constructor.js index f02c27eae..4a8731493 100644 --- a/src/entities/entity_constructor.js +++ b/src/services/becca/entity_constructor.js @@ -1,11 +1,9 @@ -const repository = require('../services/repository'); -const Note = require('../entities/note'); -const NoteRevision = require('../services/becca/entities/note_revision.js'); -const Branch = require('../entities/branch'); -const Attribute = require('../entities/attribute'); -const RecentNote = require('../services/becca/entities/recent_note.js'); -const ApiToken = require('../services/becca/entities/api_token.js'); -const cls = require('../services/cls'); +const Note = require('./entities/note'); +const NoteRevision = require('./entities/note_revision.js'); +const Branch = require('./entities/branch'); +const Attribute = require('./entities/attribute'); +const RecentNote = require('./entities/recent_note.js'); +const ApiToken = require('./entities/api_token.js'); const ENTITY_NAME_TO_ENTITY = { "attributes": Attribute, @@ -31,13 +29,9 @@ function createEntityFromRow(row) { if (row.attributeId) { entity = new Attribute(row); - - cls.setEntityToCache('attributes', row.attributeId, entity); } else if (row.noteRevisionId) { entity = new NoteRevision(row); - - cls.setEntityToCache('note_revisions', row.noteRevisionId, entity); } else if (row.branchId && row.notePath) { entity = new RecentNote(row); @@ -47,13 +41,9 @@ function createEntityFromRow(row) { } else if (row.branchId) { entity = new Branch(row); - - cls.setEntityToCache('branches', row.branchId, entity); } else if (row.noteId) { entity = new Note(row); - - cls.setEntityToCache('notes', row.noteId, entity); } else { throw new Error('Unknown entity type for row: ' + JSON.stringify(row)); diff --git a/src/services/cls.js b/src/services/cls.js index 7754da700..25f5b02c2 100644 --- a/src/services/cls.js +++ b/src/services/cls.js @@ -72,14 +72,6 @@ function reset() { clsHooked.reset(); } -function getEntityFromCache(entityName, entityId) { - return namespace.get(entityName + '-' + entityId); -} - -function setEntityToCache(entityName, entityId, entity) { - namespace.set(entityName + '-' + entityId, entity); -} - function ignoreEntityChanges() { namespace.set('ignoreEntityChanges', true); } @@ -99,7 +91,5 @@ module.exports = { clearEntityChanges, getAndClearEntityChanges, addEntityChange, - getEntityFromCache, - setEntityToCache, ignoreEntityChanges }; diff --git a/src/services/consistency_checks.js b/src/services/consistency_checks.js index 871d7ff7f..cc3a046be 100644 --- a/src/services/consistency_checks.js +++ b/src/services/consistency_checks.js @@ -5,7 +5,6 @@ const sqlInit = require('./sql_init'); const log = require('./log'); const ws = require('./ws.js'); const syncMutexService = require('./sync_mutex'); -const repository = require('./repository'); const cls = require('./cls'); const entityChangesService = require('./entity_changes.js'); const optionsService = require('./options'); @@ -459,7 +458,7 @@ class ConsistencyChecks { entity_changes.id IS NULL AND ` + (entityName === 'options' ? 'options.isSynced = 1' : '1'), ({entityId}) => { if (this.autoFix) { - const entity = repository.getEntity(`SELECT * FROM ${entityName} WHERE ${key} = ?`, [entityId]); + const entity = becca.getEntity(entityName, entityId); entityChangesService.addEntityChange({ entityName, diff --git a/src/services/entity_changes.js b/src/services/entity_changes.js index 6bd7eeb48..04ef50fbc 100644 --- a/src/services/entity_changes.js +++ b/src/services/entity_changes.js @@ -3,6 +3,7 @@ const sourceIdService = require('./source_id'); const dateUtils = require('./date_utils'); const log = require('./log'); const cls = require('./cls'); +const becca = require("./becca/becca.js"); let maxEntityChangeId = 0; @@ -82,7 +83,6 @@ function cleanupEntityChangesForMissingEntities(entityName, entityPrimaryKey) { function fillEntityChanges(entityName, entityPrimaryKey, condition = '') { try { cleanupEntityChangesForMissingEntities(entityName, entityPrimaryKey); - const repository = require("./repository.js"); sql.transactional(() => { const entityIds = sql.getColumn(`SELECT ${entityPrimaryKey} FROM ${entityName}` @@ -97,7 +97,7 @@ function fillEntityChanges(entityName, entityPrimaryKey, condition = '') { if (existingRows === 0) { createdCount++; - const entity = repository.getEntity(`SELECT * FROM ${entityName} WHERE ${entityPrimaryKey} = ?`, [entityId]); + const entity = becca.getEntity(entityName, entityId); addEntityChange({ entityName, diff --git a/src/services/export/opml.js b/src/services/export/opml.js index 8b0d8f7dd..a3a1842a4 100644 --- a/src/services/export/opml.js +++ b/src/services/export/opml.js @@ -1,6 +1,5 @@ "use strict"; -const repository = require("../repository"); const utils = require('../utils'); const becca = require("../becca/becca"); diff --git a/src/services/export/zip.js b/src/services/export/zip.js index 28c558057..814986bfc 100644 --- a/src/services/export/zip.js +++ b/src/services/export/zip.js @@ -1,7 +1,6 @@ "use strict"; const html = require('html'); -const repository = require('../repository'); const dateUtils = require('../date_utils'); const path = require('path'); const mimeTypes = require('mime-types'); diff --git a/src/services/repository.js b/src/services/repository.js deleted file mode 100644 index 84e3b0401..000000000 --- a/src/services/repository.js +++ /dev/null @@ -1,146 +0,0 @@ -"use strict"; - -const sql = require('./sql'); -const entityChangesService = require('./entity_changes.js'); -const eventService = require('./events'); -const cls = require('./cls'); -const entityConstructor = require('../entities/entity_constructor'); - -function getEntities(query, params = []) { - const rows = sql.getRows(query, params); - - return rows.map(entityConstructor.createEntityFromRow); -} - -function getEntity(query, params = []) { - const row = sql.getRowOrNull(query, params); - - if (!row) { - return null; - } - - return entityConstructor.createEntityFromRow(row); -} - -function getCachedEntity(entityName, entityId, query) { - let entity = cls.getEntityFromCache(entityName, entityId); - - if (!entity) { - entity = getEntity(query, [entityId]); - - cls.setEntityToCache(entityName, entityId, entity); - } - - return entity; -} - -/** @returns {Note|null} */ -function getNote(noteId) { - return getCachedEntity('notes', noteId, "SELECT * FROM notes WHERE noteId = ?"); -} - -/** @returns {Note[]} */ -function getNotes(noteIds) { - // this note might be optimised, but remember that it must keep the existing order of noteIds - // (important e.g. for @orderBy in search) - const notes = []; - - for (const noteId of noteIds) { - const note = getNote(noteId); - - notes.push(note); - } - - return notes; -} - -/** @returns {NoteRevision|null} */ -function getNoteRevision(noteRevisionId) { - return getCachedEntity('note_revisions', noteRevisionId, "SELECT * FROM note_revisions WHERE noteRevisionId = ?"); -} - -/** @returns {Branch|null} */ -function getBranch(branchId) { - return getCachedEntity('branches', branchId, "SELECT * FROM branches WHERE branchId = ?", [branchId]); -} - -/** @returns {Attribute|null} */ -function getAttribute(attributeId) { - return getCachedEntity('attributes', attributeId, "SELECT * FROM attributes WHERE attributeId = ?"); -} - -/** @returns {Option|null} */ -function getOption(name) { - return getEntity("SELECT * FROM options WHERE name = ?", [name]); -} - -function updateEntity(entity) { - const entityName = entity.constructor.entityName; - const primaryKeyName = entity.constructor.primaryKeyName; - - const isNewEntity = !entity[primaryKeyName]; - - if (entity.beforeSaving) { - entity.beforeSaving(); - } - - let clone; - - if (entity.getPojo) { - clone = entity.getPojo(); - } - else { - // FIXME: delete this branch after migration to becca - clone = Object.assign({}, entity); - - // this check requires that updatePojo is not static - if (entity.updatePojo) { - entity.updatePojo(clone); - } - } - - for (const key in clone) { - // !isBuffer is for images and attachments - if (clone[key] !== null && typeof clone[key] === 'object' && !Buffer.isBuffer(clone[key])) { - clone[key] = JSON.stringify(clone[key]); - } - } - - sql.transactional(() => { - sql.upsert(entityName, primaryKeyName, clone); - - if (entityName === 'recent_notes') { - return; - } - - const entityId = entity[primaryKeyName]; - - const isSynced = entityName !== 'options' || entity.isSynced; - - entityChangesService.addEntityChange({ - entityName, - entityId, - hash: entity.generateHash(), - isErased: false, - utcDateChanged: entity.getUtcDateChanged() - }, null, isSynced); - - if (!cls.isEntityEventsDisabled()) { - const eventPayload = { - entityName, - entity - }; - - if (isNewEntity && !entity.isDeleted) { - eventService.emit(eventService.ENTITY_CREATED, eventPayload); - } - - eventService.emit(entity.isDeleted ? eventService.ENTITY_DELETED : eventService.ENTITY_CHANGED, eventPayload); - } - }); -} - -module.exports = { - getEntities, - updateEntity -}; diff --git a/src/services/script.js b/src/services/script.js index 096019c16..30180a8f4 100644 --- a/src/services/script.js +++ b/src/services/script.js @@ -57,7 +57,7 @@ function executeBundle(bundle, apiParams = {}) { function executeScript(script, params, startNoteId, currentNoteId, originEntityName, originEntityId) { const startNote = becca.getNote(startNoteId); const currentNote = becca.getNote(currentNoteId); - const originEntity = becca.getEntityFromName(originEntityName, originEntityId); + const originEntity = becca.getEntity(originEntityName, originEntityId); // we're just executing an excerpt of the original frontend script in the backend context so we must // override normal note's content and it's mime type / script environment diff --git a/src/services/setup.js b/src/services/setup.js index 867510c6e..2cefe3e23 100644 --- a/src/services/setup.js +++ b/src/services/setup.js @@ -1,7 +1,6 @@ const syncService = require('./sync'); const log = require('./log'); const sqlInit = require('./sql_init'); -const repository = require('./repository'); const optionService = require('./options'); const syncOptions = require('./sync_options'); const request = require('./request'); diff --git a/src/services/sync.js b/src/services/sync.js index edb64ce9b..7355d66dd 100644 --- a/src/services/sync.js +++ b/src/services/sync.js @@ -2,7 +2,6 @@ const log = require('./log'); const sql = require('./sql'); -const sqlInit = require('./sql_init'); const optionService = require('./options'); const utils = require('./utils'); const sourceIdService = require('./source_id'); @@ -16,7 +15,7 @@ const cls = require('./cls'); const request = require('./request'); const ws = require('./ws'); const entityChangesService = require('./entity_changes.js'); -const entityConstructor = require('../entities/entity_constructor'); +const entityConstructor = require('../services/becca/entity_constructor'); let proxyToggle = true; diff --git a/src/services/sync_update.js b/src/services/sync_update.js index fe346ad21..d785377a8 100644 --- a/src/services/sync_update.js +++ b/src/services/sync_update.js @@ -2,7 +2,7 @@ const sql = require('./sql'); const log = require('./log'); const entityChangesService = require('./entity_changes.js'); const eventService = require('./events'); -const entityConstructor = require('../entities/entity_constructor'); +const entityConstructor = require("./becca/entity_constructor.js"); function updateEntity(entityChange, entity, sourceId) { // can be undefined for options with isSynced=false diff --git a/src/tools/generate_document.js b/src/tools/generate_document.js index 67a6c8936..fb3869c83 100644 --- a/src/tools/generate_document.js +++ b/src/tools/generate_document.js @@ -9,7 +9,6 @@ const noteService = require('../services/notes'); const attributeService = require('../services/attributes'); const cls = require('../services/cls'); const cloningService = require('../services/cloning'); -const repository = require('../services/repository'); const noteRevisionService = require('../services/note_revisions'); const loremIpsum = require('lorem-ipsum').loremIpsum;