server/controller/tagController.js
import winston from 'winston';
import tagDao from '../dao/tagDao';
import linkDao from '../dao/linkDao';
import ResponseUtil from '../../src/util/ResponseUtil';
import BaseProcessor from './BaseProcessor';
import TagHierarchyLogic, { createTagHierarchy } from '../logic/TagHierarchy';
import { READONLY_TAGS } from '../../src/util/TagRegistry';
class GetTagHierarchyProcessor extends BaseProcessor {
constructor(req, res, next) {
super(req, res, next, true);
}
async process() {
try {
const responseData = await TagHierarchyLogic.loadResponseData(this.data.userid);
this.res.send(responseData);
} catch (err) {
winston.loggers.get('application').error(err);
ResponseUtil.sendErrorResponse500(err, this.res);
}
this.res.end();
}
}
class PersistTagHierarchyProcessor extends BaseProcessor {
constructor(req, res, next) {
super(req, res, next, true);
}
collectBodyParameters() {
const { tree } = this.req.body;
this.data = { tree };
}
/* eslint-disable class-methods-use-this */
propertiesToValidate() {
return [{ name: 'tree' }];
}
/* eslint-enable class-methods-use-this */
validateData() {
const usedNames = {};
const usedIndexs = {};
this.data.tree.forEach((treeElement) => {
if (usedNames[treeElement.name]) {
throw new Error(`Failed to persist due to duplicate name=${treeElement.name}`);
}
if (treeElement.name === 'root' && treeElement.parent === null) {
return;
}
if (treeElement.parent === null) {
throw new Error(`Failed to persist due to null === parent=${treeElement.name}`);
}
let usedIndexForParent = usedIndexs[treeElement.parent];
if (!usedIndexForParent) {
usedIndexForParent = {};
usedIndexs[treeElement.parent] = usedIndexForParent;
}
const index = `INDEX${treeElement.index}`;
if (usedIndexForParent[index]) {
throw new Error(`Failed to persist due to duplicate index=${treeElement.index},name=${treeElement.name},parent=${treeElement.parent}`);
}
usedNames[treeElement.name] = true;
usedIndexForParent[index] = true;
if (this.data.tree.findIndex(e => e.name === treeElement.parent) === -1) {
throw new Error(`Failed to persist due to missing parent=${treeElement.parent}`);
}
});
}
async process() {
try {
this.validateData();
const rec = await tagDao.getHierarchyByUser(this.data.userid);
let recToWrite;
if (!rec) {
recToWrite = createTagHierarchy(
this.data.userid,
this.data.tree.map(e => ({ name: e.name, parent: e.parent, index: e.index })),
);
} else {
recToWrite = Object.assign({}, rec, {
tree: this.data.tree.map(e => ({ name: e.name, parent: e.parent, index: e.index })),
});
}
const { id } = await tagDao.insert(recToWrite);
this.res.send({ result: 'ok' });
winston.loggers.get('application').debug('Persisted TagHierarchy id=%s to db: %j', id, this.data);
} catch (err) {
winston.loggers.get('application').error('Failed to persist TagHierarchy. Error = %j', err);
ResponseUtil.sendErrorResponse500(err, this.res);
}
this.res.end();
}
}
class RemoveTagProcessor extends BaseProcessor {
constructor(req, res, next) {
super(req, res, next, true);
}
collectBodyParameters() {
const { name } = this.req.params;
this.data = { name };
}
/* eslint-disable class-methods-use-this */
propertiesToValidate() {
return [{ name: 'name' }];
}
/* eslint-enable class-methods-use-this */
validateData(tagHierarchyRec) {
if (READONLY_TAGS.findIndex(e => e === this.data.name) !== -1) {
throw new Error(`Cannot delete ${this.data.name} because this is a system tag.`);
}
if (tagHierarchyRec &&
tagHierarchyRec.tree.findIndex(e => e.parent === this.data.name) !== -1) {
throw new Error(`Cannot delete ${this.data.name} because it has child tags.`);
}
}
async process() {
try {
const tagHierarchyRec = await tagDao.getHierarchyByUser(this.data.userid);
this.validateData(tagHierarchyRec);
if (tagHierarchyRec) {
const recToWrite = Object.assign({}, tagHierarchyRec, {
tree: tagHierarchyRec.tree.filter(e => e.name !== this.data.name),
});
tagDao.insert(recToWrite);
}
const rows = await linkDao.listByUseridAndTag(this.data.userid, this.data.name);
const docsToWrite = rows.map((row) => {
const recLink = row.value;
recLink.tags = recLink.tags.filter(e => e !== this.data.name);
return recLink;
});
linkDao.bulk({ docs: docsToWrite });
this.res.send({ result: 'ok' });
winston.loggers.get('application').debug('Tag removed name=%s', this.data.name);
} catch (err) {
winston.loggers.get('application').error('Failed to remove Tag from TagHierarchy and LinkList. Error = %j', err);
ResponseUtil.sendErrorResponse500(err, this.res);
}
this.res.end();
}
}
export default {
getTagHierarchy: (req, res, next) => {
const glp = new GetTagHierarchyProcessor(req, res, next);
glp.doProcess();
},
persistTagHierarchy: (req, res, next) => {
const pthp = new PersistTagHierarchyProcessor(req, res, next);
pthp.doProcess();
},
removeTag: (req, res, next) => {
const pthp = new RemoveTagProcessor(req, res, next);
pthp.doProcess();
},
};