Development

* Generate error when a comment text is not found
* Save errors to SessionData as objects
* Move all search code to globalSearch publication
* Add more translation tags
This commit is contained in:
John R. Supplee 2021-01-25 15:39:36 +02:00
parent 211d27352a
commit 158a0807d9
8 changed files with 452 additions and 409 deletions

View file

@ -125,7 +125,10 @@ CardComments.textSearch = (userId, textArray) => {
const comments = CardComments.find(selector);
// eslint-disable-next-line no-console
console.log('count:', comments.count());
// console.log('count:', comments.count());
// eslint-disable-next-line no-console
// console.log('cards with comments:', comments.map(com => { return com.cardId }));
return comments;
};

View file

@ -1,5 +1,3 @@
const escapeForRegex = require('escape-string-regexp');
Cards = new Mongo.Collection('cards');
// XXX To improve pub/sub performances a card document should include a
@ -1865,355 +1863,6 @@ Cards.mutations({
},
});
Cards.globalSearch = queryParams => {
const userId = Meteor.userId();
// eslint-disable-next-line no-console
// console.log('userId:', userId);
const errors = new (class {
constructor() {
this.notFound = {
boards: [],
swimlanes: [],
lists: [],
labels: [],
users: [],
members: [],
assignees: [],
is: [],
};
this.colorMap = {};
for (const color of Boards.simpleSchema()._schema['labels.$.color']
.allowedValues) {
this.colorMap[TAPi18n.__(`color-${color}`)] = color;
}
}
hasErrors() {
for (const prop in this.notFound) {
if (this.notFound[prop].length) {
return true;
}
}
return false;
}
errorMessages() {
const messages = [];
this.notFound.boards.forEach(board => {
messages.push(TAPi18n.__('board-title-not-found', board));
});
this.notFound.swimlanes.forEach(swim => {
messages.push(TAPi18n.__('swimlane-title-not-found', swim));
});
this.notFound.lists.forEach(list => {
messages.push(TAPi18n.__('list-title-not-found', list));
});
this.notFound.labels.forEach(label => {
const color = Object.entries(this.colorMap)
.filter(value => value[1] === label)
.map(value => value[0]);
if (color.length) {
messages.push(TAPi18n.__('label-color-not-found', color[0]));
} else {
messages.push(TAPi18n.__('label-not-found', label));
}
});
this.notFound.users.forEach(user => {
messages.push(TAPi18n.__('user-username-not-found', user));
});
this.notFound.members.forEach(user => {
messages.push(TAPi18n.__('user-username-not-found', user));
});
this.notFound.assignees.forEach(user => {
messages.push(TAPi18n.__('user-username-not-found', user));
});
return messages;
}
})();
const selector = {
archived: false,
type: 'cardType-card',
boardId: { $in: Boards.userBoardIds(userId) },
swimlaneId: { $nin: Swimlanes.archivedSwimlaneIds() },
listId: { $nin: Lists.archivedListIds() },
};
if (queryParams.boards.length) {
const queryBoards = [];
queryParams.boards.forEach(query => {
const boards = Boards.userSearch(userId, {
title: new RegExp(escapeForRegex(query), 'i'),
});
if (boards.count()) {
boards.forEach(board => {
queryBoards.push(board._id);
});
} else {
errors.notFound.boards.push(query);
}
});
selector.boardId.$in = queryBoards;
}
if (queryParams.swimlanes.length) {
const querySwimlanes = [];
queryParams.swimlanes.forEach(query => {
const swimlanes = Swimlanes.find({
title: new RegExp(escapeForRegex(query), 'i'),
});
if (swimlanes.count()) {
swimlanes.forEach(swim => {
querySwimlanes.push(swim._id);
});
} else {
errors.notFound.swimlanes.push(query);
}
});
selector.swimlaneId.$in = querySwimlanes;
}
if (queryParams.lists.length) {
const queryLists = [];
queryParams.lists.forEach(query => {
const lists = Lists.find({
title: new RegExp(escapeForRegex(query), 'i'),
});
if (lists.count()) {
lists.forEach(list => {
queryLists.push(list._id);
});
} else {
errors.notFound.lists.push(query);
}
});
selector.listId.$in = queryLists;
}
if (queryParams.comments.length) {
selector._id = {
$in: CardComments.textSearch(userId, queryParams.comments).map(com => {
return com.cardId;
}),
};
}
if (queryParams.dueAt !== null) {
selector.dueAt = { $lte: new Date(queryParams.dueAt) };
}
if (queryParams.createdAt !== null) {
selector.createdAt = { $gte: new Date(queryParams.createdAt) };
}
if (queryParams.modifiedAt !== null) {
selector.modifiedAt = { $gte: new Date(queryParams.modifiedAt) };
}
const queryMembers = [];
const queryAssignees = [];
if (queryParams.users.length) {
queryParams.users.forEach(query => {
const users = Users.find({
username: query,
});
if (users.count()) {
users.forEach(user => {
queryMembers.push(user._id);
queryAssignees.push(user._id);
});
} else {
errors.notFound.users.push(query);
}
});
}
if (queryParams.members.length) {
queryParams.members.forEach(query => {
const users = Users.find({
username: query,
});
if (users.count()) {
users.forEach(user => {
queryMembers.push(user._id);
});
} else {
errors.notFound.members.push(query);
}
});
}
if (queryParams.assignees.length) {
queryParams.assignees.forEach(query => {
const users = Users.find({
username: query,
});
if (users.count()) {
users.forEach(user => {
queryAssignees.push(user._id);
});
} else {
errors.notFound.assignees.push(query);
}
});
}
if (queryMembers.length && queryAssignees.length) {
selector.$or = [
{ members: { $in: queryMembers } },
{ assignees: { $in: queryAssignees } },
];
} else if (queryMembers.length) {
selector.members = { $in: queryMembers };
} else if (queryAssignees.length) {
selector.assignees = { $in: queryAssignees };
}
if (queryParams.labels.length) {
queryParams.labels.forEach(label => {
const queryLabels = [];
let boards = Boards.userSearch(userId, {
labels: { $elemMatch: { color: label.toLowerCase() } },
});
if (boards.count()) {
boards.forEach(board => {
// eslint-disable-next-line no-console
// console.log('board:', board);
// eslint-disable-next-line no-console
// console.log('board.labels:', board.labels);
board.labels
.filter(boardLabel => {
return boardLabel.color === label.toLowerCase();
})
.forEach(boardLabel => {
queryLabels.push(boardLabel._id);
});
});
} else {
// eslint-disable-next-line no-console
// console.log('label:', label);
const reLabel = new RegExp(escapeForRegex(label), 'i');
// eslint-disable-next-line no-console
// console.log('reLabel:', reLabel);
boards = Boards.userSearch(userId, {
labels: { $elemMatch: { name: reLabel } },
});
if (boards.count()) {
boards.forEach(board => {
board.labels
.filter(boardLabel => {
return boardLabel.name.match(reLabel);
})
.forEach(boardLabel => {
queryLabels.push(boardLabel._id);
});
});
} else {
errors.notFound.labels.push(label);
}
}
selector.labelIds = { $in: queryLabels };
});
}
if (errors.hasErrors()) {
return { cards: null, errors };
}
if (queryParams.text) {
const regex = new RegExp(escapeForRegex(queryParams.text), 'i');
selector.$or = [
{ title: regex },
{ description: regex },
{ customFields: { $elemMatch: { value: regex } } },
{
_id: {
$in: CardComments.textSearch(userId, [queryParams.text]).map(
com => com.cardId,
),
},
},
];
}
// eslint-disable-next-line no-console
console.log('selector:', selector);
const projection = {
fields: {
_id: 1,
archived: 1,
boardId: 1,
swimlaneId: 1,
listId: 1,
title: 1,
type: 1,
sort: 1,
members: 1,
assignees: 1,
colors: 1,
dueAt: 1,
createdAt: 1,
modifiedAt: 1,
labelIds: 1,
},
limit: 50,
};
if (queryParams.sort === 'due') {
projection.sort = {
dueAt: 1,
boardId: 1,
swimlaneId: 1,
listId: 1,
sort: 1,
};
} else if (queryParams.sort === 'modified') {
projection.sort = {
modifiedAt: -1,
boardId: 1,
swimlaneId: 1,
listId: 1,
sort: 1,
};
} else if (queryParams.sort === 'created') {
projection.sort = {
createdAt: -1,
boardId: 1,
swimlaneId: 1,
listId: 1,
sort: 1,
};
} else if (queryParams.sort === 'system') {
projection.sort = {
boardId: 1,
swimlaneId: 1,
listId: 1,
modifiedAt: 1,
sort: 1,
};
}
const cards = Cards.find(selector, projection);
// eslint-disable-next-line no-console
console.log('count:', cards.count());
return { cards, errors };
};
//FUNCTIONS FOR creation of Activities
function updateActivities(doc, fieldNames, modifier) {

View file

@ -54,6 +54,35 @@ SessionData.attachSchema(
type: [String],
optional: true,
},
errors: {
type: [Object],
optional: true,
defaultValue: [],
},
'errors.$': {
type: new SimpleSchema({
tag: {
/**
* i18n tag
*/
type: String,
optional: false,
},
value: {
/**
* value for the tag
*/
type: String,
optional: true,
defaultValue: null,
},
color: {
type: Boolean,
optional: true,
defaultValue: false,
},
}),
},
createdAt: {
/**
* creation date of the team