2018-08-03 04:48:21 +08:00
|
|
|
"use strict";
|
|
|
|
|
|
|
|
const sql = require('../../services/sql');
|
|
|
|
const attributeService = require('../../services/attributes');
|
|
|
|
const repository = require('../../services/repository');
|
|
|
|
const Attribute = require('../../entities/attribute');
|
|
|
|
|
2018-08-06 14:59:26 +08:00
|
|
|
async function getEffectiveNoteAttributes(req) {
|
2018-08-03 04:48:21 +08:00
|
|
|
const noteId = req.params.noteId;
|
|
|
|
|
2018-08-06 14:59:26 +08:00
|
|
|
const attributes = await 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
|
|
|
|
JOIN notes ON notes.noteId = branches.parentNoteId
|
|
|
|
WHERE notes.isDeleted = 0 AND branches.isDeleted = 0
|
|
|
|
)
|
|
|
|
SELECT attributes.* FROM attributes JOIN tree ON attributes.noteId = tree.noteId
|
|
|
|
WHERE attributes.isDeleted = 0 AND (attributes.isInheritable = 1 OR attributes.noteId = ?)
|
|
|
|
ORDER BY level, noteId, position`, [noteId, 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 (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 === attr.type + '-definition' && el.name === attr.name);
|
|
|
|
|
|
|
|
if (!definitionAttr) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
const definition = definitionAttr.value;
|
|
|
|
|
|
|
|
if (definition.multiplicityType === 'multivalue') {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return filteredAttributes;
|
2018-08-03 04:48:21 +08:00
|
|
|
}
|
|
|
|
|
2018-08-06 20:43:42 +08:00
|
|
|
async function updateNoteAttribute(req) {
|
|
|
|
const noteId = req.params.noteId;
|
|
|
|
const body = req.body;
|
|
|
|
|
|
|
|
let attribute;
|
|
|
|
if (body.attributeId) {
|
|
|
|
attribute = await repository.getAttribute(body.attributeId);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
attribute = new Attribute();
|
|
|
|
attribute.noteId = noteId;
|
|
|
|
attribute.name = body.name;
|
|
|
|
attribute.type = body.type;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (attribute.noteId !== noteId) {
|
|
|
|
throw new Error(`Attribute ${body.attributeId} does not belong to note ${noteId}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
attribute.value = body.value;
|
|
|
|
|
|
|
|
await attribute.save();
|
2018-08-06 23:24:35 +08:00
|
|
|
|
|
|
|
return {
|
|
|
|
attributeId: attribute.attributeId
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
async function deleteNoteAttribute(req) {
|
|
|
|
const attributeId = req.params.attributeId;
|
|
|
|
|
|
|
|
const attribute = await repository.getAttribute(attributeId);
|
|
|
|
|
|
|
|
if (attribute) {
|
|
|
|
attribute.isDeleted = 1;
|
|
|
|
await attribute.save();
|
|
|
|
}
|
2018-08-06 20:43:42 +08:00
|
|
|
}
|
|
|
|
|
2018-08-03 04:48:21 +08:00
|
|
|
async function updateNoteAttributes(req) {
|
|
|
|
const noteId = req.params.noteId;
|
|
|
|
const attributes = req.body;
|
|
|
|
|
|
|
|
for (const attribute of attributes) {
|
|
|
|
let attributeEntity;
|
|
|
|
|
|
|
|
if (attribute.attributeId) {
|
|
|
|
attributeEntity = await repository.getAttribute(attribute.attributeId);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// if it was "created" and then immediatelly deleted, we just don't create it at all
|
|
|
|
if (attribute.isDeleted) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
attributeEntity = new Attribute();
|
|
|
|
attributeEntity.noteId = noteId;
|
|
|
|
}
|
|
|
|
|
2018-08-06 02:08:56 +08:00
|
|
|
attributeEntity.type = attribute.type;
|
2018-08-03 04:48:21 +08:00
|
|
|
attributeEntity.name = attribute.name;
|
|
|
|
attributeEntity.value = attribute.value;
|
|
|
|
attributeEntity.position = attribute.position;
|
2018-08-06 02:08:56 +08:00
|
|
|
attributeEntity.isInheritable = attribute.isInheritable;
|
2018-08-03 04:48:21 +08:00
|
|
|
attributeEntity.isDeleted = attribute.isDeleted;
|
|
|
|
|
2018-08-06 14:59:26 +08:00
|
|
|
console.log("ATTR: ", attributeEntity);
|
|
|
|
|
2018-08-03 04:48:21 +08:00
|
|
|
await attributeEntity.save();
|
|
|
|
}
|
|
|
|
|
|
|
|
return await repository.getEntities("SELECT * FROM attributes WHERE isDeleted = 0 AND noteId = ? ORDER BY position, dateCreated", [noteId]);
|
|
|
|
}
|
|
|
|
|
2018-08-03 17:11:57 +08:00
|
|
|
async function getAttributeNames(req) {
|
|
|
|
const type = req.query.type;
|
|
|
|
const query = req.query.query;
|
2018-08-03 04:48:21 +08:00
|
|
|
|
2018-08-03 17:11:57 +08:00
|
|
|
return attributeService.getAttributeNames(type, query);
|
2018-08-03 04:48:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
async function getValuesForAttribute(req) {
|
|
|
|
const attributeName = req.params.attributeName;
|
|
|
|
|
|
|
|
return await sql.getColumn("SELECT DISTINCT value FROM attributes WHERE isDeleted = 0 AND name = ? AND value != '' ORDER BY value", [attributeName]);
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
updateNoteAttributes,
|
2018-08-06 20:43:42 +08:00
|
|
|
updateNoteAttribute,
|
2018-08-06 23:24:35 +08:00
|
|
|
deleteNoteAttribute,
|
2018-08-03 17:11:57 +08:00
|
|
|
getAttributeNames,
|
2018-08-06 14:59:26 +08:00
|
|
|
getValuesForAttribute,
|
|
|
|
getEffectiveNoteAttributes
|
2018-08-03 04:48:21 +08:00
|
|
|
};
|