Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
0758a12
imp(): improved project resolver with dailyEvents
e11sy Aug 8, 2025
d4bc87f
merging repetitions on api side
slaveeks Aug 9, 2025
99899a5
fix lint
slaveeks Aug 9, 2025
0d3f614
Bump version up to 1.1.31
github-actions[bot] Aug 9, 2025
c12d698
Merge branch 'master' into refactor-project-overview
e11sy Aug 9, 2025
5d2d445
Bump version up to 1.1.31
github-actions[bot] Aug 9, 2025
bdfbc1a
Merge branch 'master' of github.com:codex-team/hawk.api.nodejs into r…
slaveeks Aug 14, 2025
107567c
Merge branch 'repetitions' of github.com:codex-team/hawk.api.nodejs i…
slaveeks Aug 14, 2025
6f01048
Bump version up to 1.1.33
github-actions[bot] Aug 14, 2025
795acae
revert some types changes
slaveeks Aug 14, 2025
5f4d987
Merge branch 'repetitions' of github.com:codex-team/hawk.api.nodejs i…
slaveeks Aug 14, 2025
ce24a61
fix lint
slaveeks Aug 14, 2025
bcd7929
fix docs
slaveeks Aug 14, 2025
cb54d3a
fixes
slaveeks Aug 14, 2025
d009928
fix get repetitions by repetition
slaveeks Aug 16, 2025
d843f3e
chore(): type defs
e11sy Aug 16, 2025
ada2a24
review changes
slaveeks Aug 16, 2025
a507bb9
fixes
slaveeks Aug 16, 2025
ff67dc7
fixes
slaveeks Aug 16, 2025
fc94873
fixes test
slaveeks Aug 16, 2025
69d64e1
fix
slaveeks Aug 16, 2025
1d85ff6
Merge pull request #518 from codex-team/repetitions
slaveeks Aug 16, 2025
33692e3
chore(): update from master
e11sy Aug 19, 2025
33e2ed8
chore(): update from master
e11sy Aug 19, 2025
1ac14b3
imp(): type defs
e11sy Aug 19, 2025
cbe8876
Bump version up to 1.1.34
github-actions[bot] Aug 19, 2025
0aa318d
fix(): fix visitedBy issue
e11sy Aug 19, 2025
17c654a
chore(): clean up
e11sy Aug 19, 2025
788de82
chore(): clean up
e11sy Aug 19, 2025
e10a613
imp(): naming and descriptions
e11sy Aug 20, 2025
f3d87ad
imp(): pass original event id to getEventRepetitions
e11sy Aug 20, 2025
24017f4
chore(): lint fix
e11sy Aug 20, 2025
5800e2a
review cghanges
slaveeks Aug 20, 2025
68166d0
Merge branch 'refactor-project-overview' of github.com:codex-team/haw…
slaveeks Aug 20, 2025
1ad7524
test addons merhing
slaveeks Aug 20, 2025
245361f
fix test
slaveeks Aug 20, 2025
8e38bac
imp(): throw exceptions
e11sy Aug 22, 2025
d4786bd
imp(): get rid of event id in repetitionsPortion
e11sy Aug 23, 2025
e52d4d1
chore(): clean up
e11sy Aug 23, 2025
ffd9040
chore(): lint fix
e11sy Aug 23, 2025
4cd5782
imp(): daily events cursor types
e11sy Aug 25, 2025
d3d61a0
fix original event without repetition in project overview
slaveeks Aug 25, 2025
af05990
Merge branch 'refactor-project-overview' of github.com:codex-team/haw…
slaveeks Aug 25, 2025
db61825
Revert "fix original event without repetition in project overview"
slaveeks Aug 25, 2025
e478c93
unwnd
slaveeks Aug 25, 2025
a65dcd4
imp(): rename
e11sy Aug 26, 2025
9e2fa3e
imp(): remove redundant db calls
e11sy Aug 26, 2025
f88331d
Merge branch 'master' into refactor-project-overview
e11sy Aug 26, 2025
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.30",
"version": "1.1.31",
"main": "index.ts",
"license": "UNLICENSED",
"scripts": {
Expand Down
80 changes: 46 additions & 34 deletions src/models/eventsFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,6 @@ const mongo = require('../mongo');
const Event = require('../models/event');
const { ObjectID } = require('mongodb');

/**
* @typedef {Object} RecentEventSchema
* @property {Event} event - event model
* @property {Number} count - recent error occurred count
* @property {String} data - error occurred date (string)
*/

/**
* @typedef {Object} EventRepetitionSchema
* @property {String} _id — repetition's identifier
Expand All @@ -22,6 +15,13 @@ const { ObjectID } = require('mongodb');
* @property {Number} timestamp - repetition's Unix timestamp
*/

/**
* @typedef {Object} DaylyEventsSchema
* @property {Event} event - original event of the daily one
* @property {EventRepetitionSchema} repetition - last repetition of the day
* @property {String | null} nextCursor - pointer to the next dailyEvent for pagination
*/

/**
* @typedef {Object} EventsFilters
* @property {boolean} [starred] - if true, events with 'starred' mark should be included to the output
Expand Down Expand Up @@ -148,16 +148,16 @@ class EventsFactory extends Factory {
* Returns events that grouped by day
*
* @param {Number} limit - events count limitations
* @param {Number} skip - certain number of documents to skip
* @param {String} paginatoinCursor - pointer to the first daily event to be selected
* @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(
async findRecentDailyEventsWithEventAndRepetition(
limit = 10,
skip = 0,
paginationCursor = '',
sort = 'BY_DATE',
filters = {},
search = ''
Expand Down Expand Up @@ -193,6 +193,13 @@ class EventsFactory extends Factory {
}

const pipeline = [
{
$match: paginationCursor ? {
_id: {
$gte: new ObjectID(paginationCursor),
}
} : {},
},
{
$sort: {
groupingTimestamp: -1,
Expand Down Expand Up @@ -241,6 +248,9 @@ class EventsFactory extends Factory {
: {};

pipeline.push(
/**
* Left outer join original event on groupHash field
*/
{
$lookup: {
from: 'events:' + this.projectId,
Expand All @@ -249,48 +259,50 @@ class EventsFactory extends Factory {
as: 'event',
},
},
{
$lookup: {
from: 'repetitions:' + this.projectId,
localField: 'lastRepetitionId',
foreignField: '_id',
as: 'repetition',
}
},
/**
* Desctruct event and repetition arrays since there are only one document in both arrays
*/
{
$unwind: '$event',
},
{
$unwind: '$repetition',
},
{
$match: {
...matchFilter,
...searchFilter,
},
},
{ $skip: skip },
{ $limit: limit },
{ $limit: limit + 1 },
{
$group: {
_id: null,
dailyInfo: { $push: '$$ROOT' },
events: { $push: '$event' },
},
},
{
$unset: 'dailyInfo.event',
$unset: 'groupHash',
}
);

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

const result = (await cursor.toArray()).shift();
const result = await cursor.toArray();

let lastEvent;

/**
* 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;
});
if (result.length === limit + 1) {
lastEvent = result.pop();
}

return result;

return {
nextCursor: lastEvent ? lastEvent._id.toString() : null,
dailyEvents: result,
};
}

/**
Expand Down
2 changes: 2 additions & 0 deletions src/models/projectsFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ export default class ProjectsFactory extends AbstractModelFactory<ProjectDBSchem
* @param id - user id
*/
public async findById(id: string): Promise<ProjectModel | null> {
console.log('id in data loader', id)

const projectData = await this.dataLoaders.projectById.load(id);

if (!projectData) {
Expand Down
2 changes: 2 additions & 0 deletions src/resolvers/event.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ module.exports = {
* @return {Promise<UserModel[]> | null}
*/
async visitedBy({ visitedBy, projectId }, _args, { factories, user }) {
console.log('visitedBy, projectId', visitedBy, projectId)

/**
* Crutch for Demo Workspace
*/
Expand Down
28 changes: 26 additions & 2 deletions src/resolvers/project.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const EventsFactory = require('../models/eventsFactory');
const ProjectToWorkspace = require('../models/projectToWorkspace');
const { dateFromObjectId } = require('../utils/dates');
const ProjectModel = require('../models/project').default;
const { composeFullRepetitionEvent } = require('../utils/merge');

const EVENTS_GROUP_HASH_INDEX_NAME = 'groupHashUnique';
const REPETITIONS_GROUP_HASH_INDEX_NAME = 'groupHash_hashed';
Expand Down Expand Up @@ -289,6 +290,7 @@ module.exports = {
*/
async event(project, { id: eventId }) {
const factory = new EventsFactory(project._id);

const event = await factory.findById(eventId);

if (!event) {
Expand Down Expand Up @@ -344,7 +346,7 @@ module.exports = {
*
* @return {Promise<RecentEventSchema[]>}
*/
async recentEvents(project, { limit, skip, sort, filters, search }) {
async dailyEventsPortion(project, { limit, cursor, sort, filters, search }) {
if (search) {
if (search.length > MAX_SEARCH_QUERY_LENGTH) {
search = search.slice(0, MAX_SEARCH_QUERY_LENGTH);
Expand All @@ -353,7 +355,29 @@ module.exports = {

const factory = new EventsFactory(project._id);

return factory.findRecent(limit, skip, sort, filters, search);
const dailyEventsPortion = await factory.findRecentDailyEventsWithEventAndRepetition(limit, cursor, sort, filters, search);

dailyEventsPortion.dailyEvents.forEach((dailyEvent) => {
const dailyEventLatestRepetition = dailyEvent.repetition;
const dailyEventOriginalEvent = dailyEvent.event;

const mergedRepetition = composeFullRepetitionEvent(dailyEventOriginalEvent, dailyEventLatestRepetition);
const stringifiedId = dailyEvent._id.toString();


delete dailyEvent.repetition;
delete dailyEvent.event;
delete dailyEvent._id;

dailyEvent.event = mergedRepetition;
dailyEvent.id = stringifiedId;

return dailyEvent;
})

console.log('daily events portion composed, ...[event]', dailyEventsPortion);

return dailyEventsPortion;
},

/**
Expand Down
71 changes: 71 additions & 0 deletions src/typeDefs/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,47 @@ enum EventsSortOrder {
BY_AFFECTED_USERS
}

"""
Pagination cursor of events portion and list of daily events
"""
type DailyEventsPortion {
"""
Cursor to the next portion of the events, null if there are no events left
"""
nextCursor: String

"""
Daily event information
"""
dailyEvents: [DailyEvent]
}

"""
Daily event information with event itself
"""
type DailyEvent {
"""
ID of the daily event
"""
id: ID!
"""
Count of events in this day
"""
count: Int!
"""
Count of the users affected by this event in this day
"""
affectedUsers: Int!
"""
Timestamp of the event grouping
"""
groupingTimestamp: Int!
"""
Event itself
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Event itself
Last repetition of this day

"""
event: Event!
}

"""
Events filters input type
"""
Expand Down Expand Up @@ -128,6 +169,36 @@ type Project {
"Search query"
search: String
): RecentEvents
"""
Portion of daily events
"""
dailyEventsPortion(
"""
Maximum number of results
"""
limit: Int! = 50

"""
Next Cursor to fetch next portion of events
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Next Cursor to fetch next portion of events
Next Cursor to fetch next portion of events. Id of the next daily event

"""
nextCursor: String

"""
Events sort order
"""
sort: EventsSortOrder = lastRepetitionTime

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

"""
Search query
"""
search: String
): DailyEventsPortion

"""
Return events that occurred after a certain timestamp
"""
Expand Down
Loading