More integration with constants and query classes

This commit is contained in:
John R. Supplee 2021-03-09 19:30:04 +02:00
parent ba00311dd4
commit 097cae1f8c
5 changed files with 153 additions and 116 deletions

View file

@ -44,9 +44,9 @@ template(name="globalSearch")
else if hasResults.get
.global-search-results-list-wrapper
if hasQueryErrors.get
div
ul
each msg in errorMessages
span.global-search-error-messages
li.global-search-error-messages
= msg
else
+resultsPaged(this)

View file

@ -1,16 +1,22 @@
import { CardSearchPagedComponent } from '../../lib/cardSearch';
import Boards from '../../../models/boards';
import moment from 'moment';
import {
OPERATOR_ASSIGNEE,
OPERATOR_BOARD,
OPERATOR_COMMENT,
OPERATOR_CREATED_AT,
OPERATOR_DUE,
OPERATOR_HAS,
OPERATOR_LABEL,
OPERATOR_LIMIT,
OPERATOR_LIST,
OPERATOR_MEMBER,
OPERATOR_MODIFIED_AT,
OPERATOR_SORT,
OPERATOR_STATUS,
OPERATOR_SWIMLANE,
OPERATOR_UNKNOWN,
OPERATOR_USER,
ORDER_ASCENDING,
ORDER_DESCENDING,
@ -36,7 +42,7 @@ import {
PREDICATE_WEEK,
PREDICATE_YEAR,
} from '../../../config/search-const';
import { QueryParams } from "../../../config/query-classes";
import { QueryErrors, QueryParams } from '../../../config/query-classes';
// const subManager = new SubsManager();
@ -80,7 +86,7 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
this.myLists = new ReactiveVar([]);
this.myLabelNames = new ReactiveVar([]);
this.myBoardNames = new ReactiveVar([]);
this.parsingErrors = [];
this.parsingErrors = new QueryErrors();
this.colorMap = null;
this.queryParams = null;
@ -119,26 +125,18 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
resetSearch() {
super.resetSearch();
this.parsingErrors = [];
this.parsingErrors = new QueryErrors();
}
errorMessages() {
if (this.parsingErrors.length) {
return this.parsingErrorMessages();
if (this.parsingErrors.hasErrors()) {
return this.parsingErrors.errorMessages();
}
return this.queryErrorMessages();
}
parsingErrorMessages() {
const messages = [];
if (this.parsingErrors.length) {
this.parsingErrors.forEach(err => {
messages.push(TAPi18n.__(err.tag, err.value));
});
}
return messages;
this.parsingErrors.errorMessages();
}
searchAllBoards(query) {
@ -188,12 +186,12 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
'operator-assignee-abbrev': OPERATOR_ASSIGNEE,
'operator-status': OPERATOR_STATUS,
'operator-due': OPERATOR_DUE,
'operator-created': 'createdAt',
'operator-modified': 'modifiedAt',
'operator-comment': 'comments',
'operator-created': OPERATOR_CREATED_AT,
'operator-modified': OPERATOR_MODIFIED_AT,
'operator-comment': OPERATOR_COMMENT,
'operator-has': OPERATOR_HAS,
'operator-sort': OPERATOR_SORT,
'operator-limit': 'limit',
'operator-limit': OPERATOR_LIMIT,
};
const predicates = {
@ -247,29 +245,6 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
// eslint-disable-next-line no-console
// console.log('operatorMap:', operatorMap);
// const params = {
// limit: this.resultsPerPage,
// // boards: [],
// // swimlanes: [],
// // lists: [],
// // users: [],
// members: [],
// assignees: [],
// // labels: [],
// status: [],
// // dueAt: null,
// createdAt: null,
// modifiedAt: null,
// comments: [],
// has: [],
// };
// params[OPERATOR_BOARD] = [];
// params[OPERATOR_DUE] = null;
// params[OPERATOR_LABEL] = [];
// params[OPERATOR_LIST] = [];
// params[OPERATOR_SWIMLANE] = [];
// params[OPERATOR_USER] = [];
const params = new QueryParams();
let text = '';
while (query) {
@ -299,7 +274,9 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
// console.log('found color:', value);
}
} else if (
[OPERATOR_DUE, 'createdAt', 'modifiedAt'].includes(operator)
[OPERATOR_DUE, OPERATOR_CREATED_AT, OPERATOR_MODIFIED_AT].includes(
operator,
)
) {
const days = parseInt(value, 10);
let duration = null;
@ -350,19 +327,22 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
value: date.format('YYYY-MM-DD'),
};
}
} else if (operator === 'dueAt' && value === PREDICATE_OVERDUE) {
} else if (
operator === OPERATOR_DUE &&
value === PREDICATE_OVERDUE
) {
value = {
operator: '$lt',
value: moment().format('YYYY-MM-DD'),
};
} else {
this.parsingErrors.push({
this.parsingErrors.addError(OPERATOR_DUE, {
tag: 'operator-number-expected',
value: { operator: op, value },
});
value = null;
continue;
}
} else if (operator === 'dueAt') {
} else if (operator === OPERATOR_DUE) {
value = {
operator: '$lt',
value: moment(moment().format('YYYY-MM-DD'))
@ -385,10 +365,11 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
negated = true;
}
if (!predicateTranslations.sorts[value]) {
this.parsingErrors.push({
this.parsingErrors.addError(OPERATOR_SORT, {
tag: 'operator-sort-invalid',
value,
});
continue;
} else {
value = {
name: predicateTranslations.sorts[value],
@ -397,10 +378,11 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
}
} else if (operator === OPERATOR_STATUS) {
if (!predicateTranslations.status[value]) {
this.parsingErrors.push({
this.parsingErrors.addError(OPERATOR_STATUS, {
tag: 'operator-status-invalid',
value,
});
continue;
} else {
value = predicateTranslations.status[value];
}
@ -412,23 +394,25 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
negated = true;
}
if (!predicateTranslations.has[value]) {
this.parsingErrors.push({
this.parsingErrors.addError(OPERATOR_HAS, {
tag: 'operator-has-invalid',
value,
});
continue;
} else {
value = {
field: predicateTranslations.has[value],
exists: !negated,
};
}
} else if (operator === 'limit') {
} else if (operator === OPERATOR_LIMIT) {
const limit = parseInt(value, 10);
if (isNaN(limit) || limit < 1) {
this.parsingErrors.push({
this.parsingErrors.addError(OPERATOR_LIMIT, {
tag: 'operator-limit-invalid',
value,
});
continue;
} else {
value = limit;
}
@ -436,7 +420,7 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
params.addPredicate(operator, value);
} else {
this.parsingErrors.push({
this.parsingErrors.addError(OPERATOR_UNKNOWN, {
tag: 'operator-unknown-error',
value: op,
});
@ -467,7 +451,7 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
this.queryParams = params;
if (this.parsingErrors.length) {
if (this.parsingErrors.hasErrors()) {
this.searching.set(false);
this.queryErrors = this.parsingErrorMessages();
this.hasResults.set(true);

View file

@ -8,9 +8,9 @@ import {
OPERATOR_SWIMLANE,
OPERATOR_USER,
} from './search-const';
import Boards from '../models/boards';
export class QueryParams {
text = '';
constructor(params = {}) {
@ -46,58 +46,81 @@ export class QueryParams {
}
export class QueryErrors {
operatorTagMap = [
[OPERATOR_BOARD, 'board-title-not-found'],
[OPERATOR_SWIMLANE, 'swimlane-title-not-found'],
[
OPERATOR_LABEL,
label => {
if (Boards.labelColors().includes(label)) {
return {
tag: 'label-color-not-found',
value: label,
color: true,
};
} else {
return {
tag: 'label-not-found',
value: label,
color: false,
};
}
},
],
[OPERATOR_LIST, 'list-title-not-found'],
[OPERATOR_COMMENT, 'comment-not-found'],
[OPERATOR_USER, 'user-username-not-found'],
[OPERATOR_ASSIGNEE, 'user-username-not-found'],
[OPERATOR_MEMBER, 'user-username-not-found'],
];
constructor() {
this.errors = {};
this._errors = {};
this.operatorTags = {};
this.operatorTagMap.forEach(([operator, tag]) => {
this.operatorTags[operator] = tag;
});
this.colorMap = Boards.colorMap();
}
addError(operator, value) {
if (!this.errors[operator]) {
this.errors[operator] = [];
addError(operator, error) {
if (!this._errors[operator]) {
this._errors[operator] = [];
}
this._errors[operator].push(error);
}
addNotFound(operator, value) {
if (typeof this.operatorTags[operator] === 'function') {
this.addError(operator, this.operatorTags[operator](value));
} else {
this.addError(operator, { tag: this.operatorTags[operator], value });
}
this.errors[operator].push(value)
}
hasErrors() {
return Object.entries(this.errors).length > 0;
return Object.entries(this._errors).length > 0;
}
errors() {
const errs = [];
Object.entries(this._errors).forEach(([operator, errors]) => {
errors.forEach(err => {
errs.push(err);
});
});
return errs;
}
errorMessages() {
const messages = [];
const operatorTags = {};
operatorTags[OPERATOR_BOARD] = 'board-title-not-found';
operatorTags[OPERATOR_SWIMLANE] = 'swimlane-title-not-found';
operatorTags[OPERATOR_LABEL] = label => {
if (Boards.labelColors().includes(label)) {
return {
tag: 'label-color-not-found',
value: label,
color: true,
};
} else {
return {
tag: 'label-not-found',
value: label,
color: false,
};
}
};
operatorTags[OPERATOR_LIST] = 'list-title-not-found';
operatorTags[OPERATOR_COMMENT] = 'comment-not-found';
operatorTags[OPERATOR_USER] = 'user-username-not-found';
operatorTags[OPERATOR_ASSIGNEE] = 'user-username-not-found';
operatorTags[OPERATOR_MEMBER] = 'user-username-not-found';
Object.entries(this.errors, ([operator, value]) => {
if (typeof operatorTags[operator] === 'function') {
messages.push(operatorTags[operator](value));
} else {
messages.push({ tag: operatorTags[operator], value: value });
}
Object.entries(this._errors).forEach(([operator, errors]) => {
errors.forEach(err => {
messages.push(TAPi18n.__(err.tag, err.value));
});
});
return messages;
}
}
@ -106,9 +129,9 @@ export class Query {
params = {};
selector = {};
projection = {};
errors = new QueryErrors();
constructor(selector, projection) {
this._errors = new QueryErrors();
if (selector) {
this.selector = selector;
}
@ -117,4 +140,16 @@ export class Query {
this.projection = projection;
}
}
hasErrors() {
return this._errors.hasErrors();
}
errors() {
return this._errors.errors();
}
errorMessages() {
return this._errors.errorMessages();
}
}

View file

@ -1,14 +1,19 @@
export const DEFAULT_LIMIT = 25;
export const OPERATOR_ASSIGNEE = 'assignee';
export const OPERATOR_COMMENT = 'comment';
export const OPERATOR_CREATED_AT = 'createdAt';
export const OPERATOR_DUE = 'dueAt';
export const OPERATOR_BOARD = 'board';
export const OPERATOR_HAS = 'has';
export const OPERATOR_LABEL = 'label';
export const OPERATOR_LIMIT = 'limit';
export const OPERATOR_LIST = 'list';
export const OPERATOR_MEMBER = 'member';
export const OPERATOR_MODIFIED_AT = 'modifiedAt';
export const OPERATOR_SORT = 'sort';
export const OPERATOR_STATUS = 'status';
export const OPERATOR_SWIMLANE = 'swimlane';
export const OPERATOR_UNKNOWN = 'unknown';
export const OPERATOR_USER = 'user';
export const ORDER_ASCENDING = 'asc';
export const ORDER_DESCENDING = 'des';

View file

@ -9,12 +9,14 @@ import ChecklistItems from '../../models/checklistItems';
import SessionData from '../../models/usersessiondata';
import CustomFields from '../../models/customFields';
import {
DEFAULT_LIMIT,
OPERATOR_ASSIGNEE,
OPERATOR_BOARD,
OPERATOR_COMMENT,
OPERATOR_DUE,
OPERATOR_HAS,
OPERATOR_LABEL,
OPERATOR_LIMIT,
OPERATOR_LIST,
OPERATOR_MEMBER,
OPERATOR_SORT,
@ -81,7 +83,7 @@ Meteor.publish('globalSearch', function(sessionId, params) {
check(params, Object);
// eslint-disable-next-line no-console
// console.log('queryParams:', queryParams);
console.log('queryParams:', params);
return findCards(sessionId, buildQuery(new QueryParams(params)));
});
@ -164,7 +166,7 @@ function buildSelector(queryParams) {
queryBoards.push(board._id);
});
} else {
errors.addError(OPERATOR_BOARD, query);
errors.addNotFound(OPERATOR_BOARD, query);
}
});
@ -182,7 +184,7 @@ function buildSelector(queryParams) {
querySwimlanes.push(swim._id);
});
} else {
errors.addError(OPERATOR_SWIMLANE, query);
errors.addNotFound(OPERATOR_SWIMLANE, query);
}
});
@ -204,7 +206,7 @@ function buildSelector(queryParams) {
queryLists.push(list._id);
});
} else {
errors.addError(OPERATOR_LIST, query);
errors.addNotFound(OPERATOR_LIST, query);
}
});
@ -216,7 +218,9 @@ function buildSelector(queryParams) {
}
if (queryParams.hasOperator(OPERATOR_COMMENT)) {
const cardIds = CardComments.textSearch(userId, queryParams.getPredicates(OPERATOR_COMMENT)).map(
const cardIds = CardComments.textSearch(
userId,
queryParams.getPredicates(OPERATOR_COMMENT),
com => {
return com.cardId;
},
@ -225,7 +229,7 @@ function buildSelector(queryParams) {
selector._id = { $in: cardIds };
} else {
queryParams.getPredicates(OPERATOR_COMMENT).forEach(comment => {
errors.addError(OPERATOR_COMMENT, comment);
errors.addNotFound(OPERATOR_COMMENT, comment);
});
}
}
@ -238,7 +242,7 @@ function buildSelector(queryParams) {
}
});
const queryUsers = {}
const queryUsers = {};
queryUsers[OPERATOR_ASSIGNEE] = [];
queryUsers[OPERATOR_MEMBER] = [];
@ -253,7 +257,7 @@ function buildSelector(queryParams) {
queryUsers[OPERATOR_ASSIGNEE].push(user._id);
});
} else {
errors.addError(OPERATOR_USER, query);
errors.addNotFound(OPERATOR_USER, query);
}
});
}
@ -269,13 +273,16 @@ function buildSelector(queryParams) {
queryUsers[key].push(user._id);
});
} else {
errors.addError(key, query);
errors.addNotFound(key, query);
}
});
}
});
if (queryUsers[OPERATOR_MEMBER].length && queryUsers[OPERATOR_ASSIGNEE].length) {
if (
queryUsers[OPERATOR_MEMBER].length &&
queryUsers[OPERATOR_ASSIGNEE].length
) {
selector.$and.push({
$or: [
{ members: { $in: queryUsers[OPERATOR_MEMBER] } },
@ -334,7 +341,7 @@ function buildSelector(queryParams) {
});
});
} else {
errors.addError(OPERATOR_LABEL, label);
errors.addNotFound(OPERATOR_LABEL, label);
}
}
@ -441,7 +448,7 @@ function buildSelector(queryParams) {
const query = new Query();
query.selector = selector;
query.params = queryParams;
query.errors = errors;
query._errors = errors;
return query;
}
@ -451,9 +458,9 @@ function buildProjection(query) {
if (query.params.skip) {
skip = query.params.skip;
}
let limit = 25;
if (query.params.limit) {
limit = query.params.limit;
let limit = DEFAULT_LIMIT;
if (query.params.hasOperator(OPERATOR_LIMIT)) {
limit = query.params.getPredicate(OPERATOR_LIMIT);
}
const projection = {
@ -485,9 +492,12 @@ function buildProjection(query) {
limit,
};
if (query.params[OPERATOR_SORT]) {
const order = query.params[OPERATOR_SORT].order === ORDER_ASCENDING ? 1 : -1;
switch (query.params[OPERATOR_SORT].name) {
if (query.params.hasOperator(OPERATOR_SORT)) {
const order =
query.params.getPredicate(OPERATOR_SORT).order === ORDER_ASCENDING
? 1
: -1;
switch (query.params.getPredicate(OPERATOR_SORT).name) {
case PREDICATE_DUE_AT:
projection.sort = {
dueAt: order,
@ -586,12 +596,13 @@ function findCards(sessionId, query) {
// eslint-disable-next-line no-console
// console.log('projection:', projection);
let cards;
if (!query.errors || !query.errors.hasErrors()) {
if (!query.hasErrors()) {
cards = Cards.find(query.selector, query.projection);
}
// eslint-disable-next-line no-console
// console.log('count:', cards.count());
console.log(query);
const update = {
$set: {
totalHits: 0,
@ -600,13 +611,15 @@ function findCards(sessionId, query) {
cards: [],
selector: SessionData.pickle(query.selector),
projection: SessionData.pickle(query.projection),
errors: query.errors.errorMessages(),
errors: query.errors(),
},
};
// if (errors) {
// update.$set.errors = errors.errorMessages();
// update.$set.errors = errors.errors();
// }
console.log('errors:', query.errors());
if (cards) {
update.$set.totalHits = cards.count();
update.$set.lastHit =