Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "hawk.api",
"version": "1.1.14",
"version": "1.1.15",
"main": "index.ts",
"license": "UNLICENSED",
"scripts": {
Expand Down
125 changes: 63 additions & 62 deletions src/models/eventsFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,15 +149,23 @@ class EventsFactory extends Factory {
* @param {Number} skip - certain number of documents to skip
* @param {'BY_DATE' | 'BY_COUNT'} sort - events sort order
* @param {EventsFilters} filters - marks by which events should be filtered
* @param {String} search - Search query
*
* @return {RecentEventSchema[]}
*/
async findRecent(
limit = 10,
skip = 0,
sort = 'BY_DATE',
filters = {}
filters = {},
search = ''
) {
if (typeof search !== 'string') {
throw new Error('Search parameter must be a string');
}

const escapedSearch = search.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');

limit = this.validateLimit(limit);

switch (sort) {
Expand All @@ -184,71 +192,64 @@ class EventsFactory extends Factory {
},
];

/**
* If some events should be omitted, use alternative pipeline
*/
if (Object.values(filters).length > 0) {
pipeline.push(
/**
* Lookup events object for each daily event
*/
{
$lookup: {
from: 'events:' + this.projectId,
localField: 'groupHash',
foreignField: 'groupHash',
as: 'event',
const searchFilter = search.trim().length > 0
? {
$or: [
{
'event.payload.title': {
$regex: escapedSearch,
$options: 'i',
},
},
},
{
$unwind: '$event',
},
/**
* Match filters
*/
{
$match: {
...Object.fromEntries(
Object
.entries(filters)
.map(([mark, exists]) => [`event.marks.${mark}`, { $exists: exists } ])
),
{
'event.payload.backtrace.file': {
$regex: escapedSearch,
$options: 'i',
},
},
],
}
: {};

const matchFilter = filters
? Object.fromEntries(
Object
.entries(filters)
.map(([mark, exists]) => [`event.marks.${mark}`, { $exists: exists } ])
)
: {};

pipeline.push(
{
$lookup: {
from: 'events:' + this.projectId,
localField: 'groupHash',
foreignField: 'groupHash',
as: 'event',
},
{ $skip: skip },
{ $limit: limit },
{
$group: {
_id: null,
dailyInfo: { $push: '$$ROOT' },
events: { $push: '$event' },
},
},
{
$unwind: '$event',
},
{
$match: {
...matchFilter,
...searchFilter,
},
{
$unset: 'dailyInfo.event',
}
);
} else {
pipeline.push(
{ $skip: skip },
{ $limit: limit },
{
$group: {
_id: null,
groupHash: { $addToSet: '$groupHash' },
dailyInfo: { $push: '$$ROOT' },
},
},
{ $skip: skip },
{ $limit: limit },
{
$group: {
_id: null,
dailyInfo: { $push: '$$ROOT' },
events: { $push: '$event' },
},
{
$lookup: {
from: 'events:' + this.projectId,
localField: 'groupHash',
foreignField: 'groupHash',
as: 'events',
},
}
);
}
},
{
$unset: 'dailyInfo.event',
}
);

const cursor = this.getCollection(this.TYPES.DAILY_EVENTS).aggregate(pipeline);

Expand Down Expand Up @@ -316,7 +317,7 @@ class EventsFactory extends Factory {
});

/**
* Group events using 'groupByTimestamp:NNNNNNNN' key
* Group events using 'groupingTimestamp:NNNNNNNN' key
* @type {ProjectChartItem[]}
*/
const groupedData = groupBy('groupingTimestamp')(dailyEvents);
Expand Down
11 changes: 9 additions & 2 deletions src/resolvers/project.js
Original file line number Diff line number Diff line change
Expand Up @@ -304,13 +304,20 @@ module.exports = {
* @param {Number} skip - certain number of documents to skip
* @param {'BY_DATE' | 'BY_COUNT'} sort - events sort order
* @param {EventsFilters} filters - marks by which events should be filtered
* @param {String} search - search query
*
* @return {Promise<RecentEventSchema[]>}
*/
async recentEvents(project, { limit, skip, sort, filters }) {
async recentEvents(project, { limit, skip, sort, filters, search }) {
if (search) {
if (search.length > 100) {
throw new UserInputError('Search query is too long. Maximum length is 100 characters');
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trim instead

}
}

const factory = new EventsFactory(project._id);

return factory.findRecent(limit, skip, sort, filters);
return factory.findRecent(limit, skip, sort, filters, search);
},

/**
Expand Down
11 changes: 11 additions & 0 deletions src/typeDefs/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ type Project {

"Event marks by which events should be sorted"
filters: EventsFiltersInput

"Search query"
search: String
): RecentEvents
"""
Return events that occurred after a certain timestamp
Expand Down Expand Up @@ -230,4 +233,12 @@ extend type Mutation {
projectId: ID!
): DateTime! @requireUserInWorkspace
}

input EventsFilter {
"""
Search query string. Only alphanumeric characters, spaces, and some special characters are allowed.
Max length: 100 characters
"""
search: String
}
`;
Loading