diff --git a/.gitignore b/.gitignore index b0e31773..8d7674dc 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ uploads globalConfig.json coverage tls +.history \ No newline at end of file diff --git a/package.json b/package.json index 4f47295e..f9b32ca1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hawk.api", - "version": "1.1.28", + "version": "1.1.29", "main": "index.ts", "license": "UNLICENSED", "scripts": { diff --git a/src/models/eventsFactory.js b/src/models/eventsFactory.js index 865dc9c7..8c636505 100644 --- a/src/models/eventsFactory.js +++ b/src/models/eventsFactory.js @@ -26,6 +26,8 @@ const { ObjectID } = require('mongodb'); * @property {boolean} [starred] - if true, events with 'starred' mark should be included to the output * @property {boolean} [resolved] - if true, events with 'resolved' should be included to the output * @property {boolean} [ignored] - if true, events with 'ignored' mark should be included to the output + * @property {string|number} [dateFrom] + * @property {string|number} [dateTo] */ /** @@ -149,8 +151,8 @@ class EventsFactory extends Factory { * @param {Number} limit - events count limitations * @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 + * @param {EventsFilters} filters - filter object + * @param {String} search - search query * * @return {RecentEventSchema[]} */ @@ -231,13 +233,40 @@ class EventsFactory extends Factory { } : {}; - const matchFilter = filters - ? Object.fromEntries( - Object - .entries(filters) - .map(([mark, exists]) => [`event.marks.${mark}`, { $exists: exists } ]) - ) - : {}; + const matchFilter = { + ...searchFilter, + }; + + ['starred', 'resolved', 'ignored'].forEach((mark) => { + if (typeof filters[mark] === 'boolean') { + matchFilter[`event.marks.${mark}`] = { $exists: filters[mark] }; + } + }); + + // Filter by date (groupingTimestamp) + if (filters.dateFrom || filters.dateTo) { + matchFilter.groupingTimestamp = {}; + + if (filters.dateFrom) { + const from = typeof filters.dateFrom === 'string' + ? Math.floor(new Date(filters.dateFrom).getTime() / 1000) + : filters.dateFrom; + + matchFilter.groupingTimestamp.$gte = from; + } + + if (filters.dateTo) { + const to = typeof filters.dateTo === 'string' + ? Math.floor(new Date(filters.dateTo).getTime() / 1000) + : filters.dateTo; + + matchFilter.groupingTimestamp.$lte = to; + } + + if (Object.keys(matchFilter.groupingTimestamp).length === 0) { + delete matchFilter.groupingTimestamp; + } + } pipeline.push( { @@ -252,10 +281,7 @@ class EventsFactory extends Factory { $unwind: '$event', }, { - $match: { - ...matchFilter, - ...searchFilter, - }, + $match: matchFilter, }, { $skip: skip }, { $limit: limit }, @@ -272,17 +298,16 @@ class EventsFactory extends Factory { ); const cursor = this.getCollection(this.TYPES.DAILY_EVENTS).aggregate(pipeline); - const result = (await cursor.toArray()).shift(); /** - * aggregation can return empty array so that - * result can be undefined - * - * for that we check result existence - * - * extra field `projectId` needs to satisfy GraphQL query - */ + * aggregation can return empty array so that + * result can be undefined + * + * for that we check result existence + * + * extra field `projectId` needs to satisfy GraphQL query + */ if (result && result.events) { result.events.forEach(event => { event.projectId = this.projectId; diff --git a/src/typeDefs/project.ts b/src/typeDefs/project.ts index f0fab0e2..cd824e40 100644 --- a/src/typeDefs/project.ts +++ b/src/typeDefs/project.ts @@ -16,17 +16,29 @@ Events filters input type """ input EventsFiltersInput { """ - If True, includes events with resolved mark to the output + If true, includes events with resolved mark """ resolved: Boolean + """ - If True, includes events with starred mark to the output + If true, includes events with starred mark """ starred: Boolean + """ - If True, includes events with ignored mark to the output + If true, includes events with ignored mark """ ignored: Boolean + + """ + Include events with groupingTimestamp >= dateFrom (ISO or timestamp in seconds) + """ + dateFrom: Timestamp + + """ + Include events with groupingTimestamp <= dateTo (ISO or timestamp in seconds) + """ + dateTo: Timestamp } """ @@ -122,12 +134,17 @@ type Project { "Events sort order" sort: EventsSortOrder = lastRepetitionTime - "Event marks by which events should be sorted" + """ + Filters for narrowing down the event results: + - By marks (e.g., starred, resolved) + - By date range (dateFrom / dateTo) + """ filters: EventsFiltersInput "Search query" search: String ): RecentEvents + """ Return events that occurred after a certain timestamp """