diff --git a/src/public/javascripts/entities/note_short.js b/src/public/javascripts/entities/note_short.js index 6af9c3283..6c727273d 100644 --- a/src/public/javascripts/entities/note_short.js +++ b/src/public/javascripts/entities/note_short.js @@ -153,31 +153,76 @@ class NoteShort { return await this.treeCache.getNotes(this.children); } + /** + * @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 + */ + getOwnedAttributes(type, name) { + const attrs = this.attributes + .map(attributeId => this.treeCache.attributes[attributeId]) + .filter(attr => !!attr); + + return this.__filterAttrs(attrs, type, name) + } + /** * @param {string} [type] - (optional) attribute type to filter * @param {string} [name] - (optional) attribute name to filter * @returns {Promise} all note's attributes, including inherited ones */ async getAttributes(type, name) { - if (!this.__attributeCache) { - this.__attributeCache = (await server.get('notes/' + this.noteId + '/attributes')) - .map(attrRow => new Attribute(this.treeCache, attrRow)); + const ownedAttributes = this.getOwnedAttributes(); + + const attrArrs = [ + ownedAttributes + ]; + + for (const templateAttr of ownedAttributes.filter(oa => oa.type === 'relation' && oa.name === 'template')) { + const templateNote = await this.treeCache.getNote(templateAttr.value); + + if (templateNote) { + attrArrs.push(await templateNote.getAttributes()); + } } + if (this.noteId !== 'root') { + for (const parentNote of await this.getParentNotes()) { + attrArrs.push(await parentNote.getInheritableAttributes()); + } + } + + const attributes = attrArrs.flat(); + + return this.__filterAttrs(attributes, type, name); + } + + __filterAttrs(attributes, type, name) { 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(); + return attributes.filter(attr => attr.type === type && attr.name === name); + } else if (type) { + return attributes.filter(attr => attr.type === type); + } else if (name) { + return attributes.filter(attr => attr.name === name); + } else { + return attributes; } } + async getInheritableAttributes() { + const attrs = await this.getAttributes(); + + return attrs.filter(attr => attr.isInheritable); + } + + /** + * @param {string} [name] - label name to filter + * @returns {Attribute[]} all note's labels (attributes with type label), including inherited ones + */ + getOwnedLabels(name) { + return this.getOwnedAttributes(LABEL, name); + } + /** * @param {string} [name] - label name to filter * @returns {Promise} all note's labels (attributes with type label), including inherited ones @@ -194,6 +239,14 @@ class NoteShort { return await this.getAttributes(LABEL_DEFINITION, name); } + /** + * @param {string} [name] - relation name to filter + * @returns {Attribute[]} all note's relations (attributes with type relation), including inherited ones + */ + getOwnedRelations(name) { + return this.getOwnedAttributes(RELATION, name); + } + /** * @param {string} [name] - relation name to filter * @returns {Promise} all note's relations (attributes with type relation), including inherited ones @@ -219,15 +272,46 @@ class NoteShort { return !!await 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 (including 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. + */ + getOwnedAttribute(type, name) { + const attributes = this.getOwnedAttributes(type, name); + + return attributes.length > 0 ? attributes[0] : 0; + } + /** * @param {string} type - attribute type (label, relation, etc.) * @param {string} name - attribute name * @returns {Promise} 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. */ async getAttribute(type, name) { - const attributes = await this.getAttributes(); + const attributes = await this.getAttributes(type, name); - return attributes.find(attr => attr.type === type && attr.name === name); + return attributes.length > 0 ? attributes[0] : 0; + } + + /** + * @param {string} type - attribute type (label, relation, etc.) + * @param {string} name - attribute name + * @returns {string} 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; } /** @@ -241,36 +325,72 @@ class NoteShort { return attr ? attr.value : null; } + /** + * @param {string} name - label name + * @returns {boolean} true if label exists (excluding inherited) + */ + hasOwnedLabel(name) { return this.hasOwnedAttribute(LABEL, name); } + /** * @param {string} name - label name * @returns {Promise} true if label exists (including inherited) */ async hasLabel(name) { return await this.hasAttribute(LABEL, name); } + /** + * @param {string} name - relation name + * @returns {boolean} true if relation exists (excluding inherited) + */ + hasOwnedRelation(name) { return this.hasOwnedAttribute(RELATION, name); } + /** * @param {string} name - relation name * @returns {Promise} true if relation exists (including inherited) */ async hasRelation(name) { return await this.hasAttribute(RELATION, name); } + /** + * @param {string} name - label name + * @returns {Attribute} label if it exists, null otherwise + */ + getOwnedLabel(name) { return this.getOwnedAttribute(LABEL, name); } + /** * @param {string} name - label name * @returns {Promise} label if it exists, null otherwise */ async getLabel(name) { return await this.getAttribute(LABEL, name); } + /** + * @param {string} name - relation name + * @returns {Attribute} relation if it exists, null otherwise + */ + getOwnedRelation(name) { return this.getOwnedAttribute(RELATION, name); } + /** * @param {string} name - relation name * @returns {Promise} relation if it exists, null otherwise */ async getRelation(name) { return await this.getAttribute(RELATION, name); } + /** + * @param {string} name - label name + * @returns {string} label value if label exists, null otherwise + */ + getOwnedLabelValue(name) { return this.getOwnedAttributeValue(LABEL, name); } + /** * @param {string} name - label name * @returns {Promise} label value if label exists, null otherwise */ async getLabelValue(name) { return await this.getAttributeValue(LABEL, name); } + /** + * @param {string} name - relation name + * @returns {string} relation value if relation exists, null otherwise + */ + getOwnedRelationValue(name) { return this.getOwnedAttributeValue(RELATION, name); } + /** * @param {string} name - relation name * @returns {Promise} relation value if relation exists, null otherwise @@ -313,11 +433,11 @@ class NoteShort { /** * Get relations which target this note * - * @returns {Promise} + * @returns {Attribute[]} */ - async getTargetRelations() { - return (await server.get('notes/' + this.noteId + '/target-relations')) - .map(attrRow => new Attribute(this.treeCache, attrRow)); + getTargetRelations() { + return this.targetRelations + .map(attributeId => this.treeCache.attributes[attributeId]); } get toString() { @@ -327,8 +447,6 @@ class NoteShort { get dto() { const dto = Object.assign({}, this); delete dto.treeCache; - delete dto.archived; - delete dto.__attributeCache; return dto; } diff --git a/src/routes/api/attributes.js b/src/routes/api/attributes.js index 5a058fff0..ac751dab1 100644 --- a/src/routes/api/attributes.js +++ b/src/routes/api/attributes.js @@ -157,12 +157,6 @@ async function deleteRelation(req) { } } -async function getTargetRelations(req) { - const note = await repository.getNote(req.params.noteId); - - return await note.getTargetRelations(); -} - module.exports = { updateNoteAttributes, updateNoteAttribute, @@ -171,6 +165,5 @@ module.exports = { getValuesForAttribute, getEffectiveNoteAttributes, createRelation, - deleteRelation, - getTargetRelations + deleteRelation }; \ No newline at end of file diff --git a/src/routes/routes.js b/src/routes/routes.js index 2d4e333fc..626046346 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -168,7 +168,6 @@ function register(app) { apiRoute(DELETE, '/api/notes/:noteId/attributes/:attributeId', attributesRoute.deleteNoteAttribute); apiRoute(GET, '/api/attributes/names', attributesRoute.getAttributeNames); apiRoute(GET, '/api/attributes/values/:attributeName', attributesRoute.getValuesForAttribute); - apiRoute(GET, '/api/notes/:noteId/target-relations', attributesRoute.getTargetRelations); apiRoute(POST, '/api/notes/:noteId/link-map', linkMapRoute.getLinkMap);