Skip to content

Commit 1d85ff6

Browse files
authored
Merge pull request #518 from codex-team/repetitions
refactor(repetitions): merging on api side
2 parents 0eced98 + 69d64e1 commit 1d85ff6

File tree

7 files changed

+248
-190
lines changed

7 files changed

+248
-190
lines changed

package.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "hawk.api",
3-
"version": "1.1.32",
3+
"version": "1.1.33",
44
"main": "index.ts",
55
"license": "UNLICENSED",
66
"scripts": {
@@ -44,8 +44,6 @@
4444
"@types/debug": "^4.1.5",
4545
"@types/escape-html": "^1.0.0",
4646
"@types/graphql-upload": "^8.0.11",
47-
"@types/lodash.clonedeep": "^4.5.9",
48-
"@types/lodash.mergewith": "^4.6.9",
4947
"@types/jsonwebtoken": "^8.3.5",
5048
"@types/lodash.clonedeep": "^4.5.9",
5149
"@types/lodash.mergewith": "^4.6.9",

src/models/eventsFactory.js

Lines changed: 144 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const Factory = require('./modelFactory');
66
const mongo = require('../mongo');
77
const Event = require('../models/event');
88
const { ObjectID } = require('mongodb');
9+
const { composeEventPayloadWithRepetition } = require('../utils/merge');
910

1011
/**
1112
* @typedef {Object} RecentEventSchema
@@ -287,6 +288,7 @@ class EventsFactory extends Factory {
287288
if (result && result.events) {
288289
result.events.forEach(event => {
289290
event.projectId = this.projectId;
291+
event.firstAppearanceTimestamp = event.timestamp;
290292
});
291293
}
292294

@@ -391,23 +393,33 @@ class EventsFactory extends Factory {
391393
/**
392394
* Returns Event repetitions
393395
*
394-
* @param {string|ObjectID} eventId - Event's id
396+
* @param {string|ObjectID} eventId - Event's id (may be repetition id)
395397
* @param {Number} limit - count limitations
396-
* @param {Number} skip - selection offset
398+
* @param {Number} cursor - pointer to the next repetition
397399
*
398400
* @return {EventRepetitionSchema[]}
399401
*
400402
* @todo move to Repetitions(?) model
401403
*/
402-
async getEventRepetitions(eventId, limit = 10, skip = 0) {
404+
async getEventRepetitions(eventId, limit = 10, cursor = null) {
403405
limit = this.validateLimit(limit);
404-
skip = this.validateSkip(skip);
406+
407+
cursor = cursor ? new ObjectID(cursor) : null;
408+
409+
const result = {
410+
repetitions: [],
411+
nextCursor: null,
412+
};
405413

406414
/**
407415
* Get original event
408416
* @type {EventSchema}
409417
*/
410-
const eventOriginal = await this.findById(eventId);
418+
const eventOriginal = await this._findOriginalEvent(eventId);
419+
420+
if (!eventOriginal) {
421+
return result;
422+
}
411423

412424
/**
413425
* Collect repetitions
@@ -416,13 +428,26 @@ class EventsFactory extends Factory {
416428
const repetitions = await this.getCollection(this.TYPES.REPETITIONS)
417429
.find({
418430
groupHash: eventOriginal.groupHash,
431+
_id: cursor ? { $lte: cursor } : { $exists: true },
419432
})
420433
.sort({ _id: -1 })
421-
.limit(limit)
422-
.skip(skip)
434+
.limit(limit + 1)
423435
.toArray();
424436

425-
const isLastPortion = repetitions.length < limit && skip === 0;
437+
if (repetitions.length === limit + 1) {
438+
result.nextCursor = repetitions.pop()._id;
439+
}
440+
441+
for (const repetition of repetitions) {
442+
const event = this._composeEventWithRepetition(eventOriginal, repetition);
443+
444+
result.repetitions.push({
445+
...event,
446+
projectId: this.projectId,
447+
});
448+
}
449+
450+
const isLastPortion = result.nextCursor === null;
426451

427452
/**
428453
* For last portion:
@@ -434,16 +459,15 @@ class EventsFactory extends Factory {
434459
* @type {EventRepetitionSchema}
435460
*/
436461
const firstRepetition = {
437-
_id: eventOriginal._id,
438-
payload: eventOriginal.payload,
439-
groupHash: eventOriginal.groupHash,
440-
timestamp: eventOriginal.timestamp,
462+
...eventOriginal,
463+
firstAppearanceTimestamp: eventOriginal.timestamp,
464+
projectId: this.projectId,
441465
};
442466

443-
repetitions.push(firstRepetition);
467+
result.repetitions.push(firstRepetition);
444468
}
445469

446-
return repetitions;
470+
return result;
447471
}
448472

449473
/**
@@ -455,10 +479,33 @@ class EventsFactory extends Factory {
455479
* @todo move to Repetitions(?) model
456480
*/
457481
async getEventRepetition(repetitionId) {
458-
return this.getCollection(this.TYPES.REPETITIONS)
482+
const repetition = await this.getCollection(this.TYPES.REPETITIONS)
459483
.findOne({
460484
_id: ObjectID(repetitionId),
461485
});
486+
487+
if (!repetition) {
488+
/**
489+
* If repetition is not found, it can mean that client is trying to get original event
490+
*/
491+
const event = await this.findById(repetitionId);
492+
493+
return event ? {
494+
...event,
495+
firstAppearanceTimestamp: event.timestamp,
496+
} : null;
497+
}
498+
499+
const originalEvent = await this.getCollection(this.TYPES.EVENTS)
500+
.findOne({
501+
groupHash: repetition.groupHash,
502+
});
503+
504+
if (!originalEvent) {
505+
return null;
506+
}
507+
508+
return this._composeEventWithRepetition(originalEvent, repetition);
462509
}
463510

464511
/**
@@ -483,7 +530,11 @@ class EventsFactory extends Factory {
483530
* @returns {Release|null}
484531
*/
485532
async getEventRelease(eventId) {
486-
const eventOriginal = await this.findById(eventId);
533+
const eventOriginal = await this._findOriginalEvent(eventId);
534+
535+
if (!eventOriginal) {
536+
return null;
537+
}
487538

488539
const release = await mongo.databases.events.collection(this.TYPES.RELEASES).findOne({
489540
release: eventOriginal.payload.release,
@@ -502,9 +553,15 @@ class EventsFactory extends Factory {
502553
* @return {Promise<void>}
503554
*/
504555
async visitEvent(eventId, userId) {
556+
const event = await this._findOriginalEvent(eventId);
557+
558+
if (!event) {
559+
return null;
560+
}
561+
505562
return this.getCollection(this.TYPES.EVENTS)
506563
.updateOne(
507-
{ _id: new ObjectID(eventId) },
564+
{ _id: new ObjectID(event._id) },
508565
{ $addToSet: { visitedBy: new ObjectID(userId) } }
509566
);
510567
}
@@ -519,9 +576,15 @@ class EventsFactory extends Factory {
519576
*/
520577
async toggleEventMark(eventId, mark) {
521578
const collection = this.getCollection(this.TYPES.EVENTS);
522-
const query = { _id: new ObjectID(eventId) };
523579

524-
const event = await collection.findOne(query);
580+
const event = await this._findOriginalEvent(eventId);
581+
582+
if (!event) {
583+
return null;
584+
}
585+
586+
const query = { _id: new ObjectID(event._id) };
587+
525588
const markKey = `marks.${mark}`;
526589

527590
let update;
@@ -571,13 +634,74 @@ class EventsFactory extends Factory {
571634
*/
572635
async updateAssignee(eventId, assignee) {
573636
const collection = this.getCollection(this.TYPES.EVENTS);
574-
const query = { _id: new ObjectID(eventId) };
637+
638+
const event = await this._findOriginalEvent(eventId);
639+
640+
if (!event) {
641+
return null;
642+
}
643+
644+
const query = { _id: new ObjectID(event._id) };
645+
575646
const update = {
576647
$set: { assignee: assignee },
577648
};
578649

579650
return collection.updateOne(query, update);
580651
}
652+
653+
/**
654+
* Find original event by eventId. If event is not found directly,
655+
* try to find it as repetition and get original event by groupHash
656+
*
657+
* @param {string|ObjectID} eventId - event's id, may be repetition id
658+
* @returns {Promise<Event|null>} original event or null if not found
659+
*/
660+
async _findOriginalEvent(eventId) {
661+
let originalEvent;
662+
663+
/**
664+
* Try to find it by repetitionId
665+
*/
666+
const repetition = await this.getCollection(this.TYPES.REPETITIONS)
667+
.findOne({
668+
_id: new ObjectID(eventId),
669+
});
670+
671+
/**
672+
* If repetition is not found by eventId, try to find it by eventId
673+
*/
674+
if (!repetition) {
675+
originalEvent = await this.getCollection(this.TYPES.EVENTS)
676+
.findOne({
677+
_id: new ObjectID(eventId),
678+
});
679+
} else {
680+
originalEvent = await this.getCollection(this.TYPES.EVENTS)
681+
.findOne({
682+
groupHash: repetition.groupHash,
683+
});
684+
}
685+
686+
return originalEvent;
687+
}
688+
689+
/**
690+
* Compose event with repetition
691+
*
692+
* @param {Event} event - event
693+
* @param {Repetition} repetition - repetition
694+
* @returns {Event} event merged with repetition
695+
*/
696+
_composeEventWithRepetition(event, repetition) {
697+
return {
698+
...event,
699+
_id: repetition._id,
700+
firstAppearanceTimestamp: event.timestamp,
701+
timestamp: repetition.timestamp,
702+
payload: composeEventPayloadWithRepetition(event.payload, repetition),
703+
};
704+
}
581705
}
582706

583707
module.exports = EventsFactory;

src/resolvers/event.js

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,39 +18,22 @@ module.exports = {
1818
},
1919
},
2020
Event: {
21-
/**
22-
* Returns Event with concrete repetition
23-
*
24-
* @param {string} eventId - id of Event of which repetition requested
25-
* @param {string} projectId - projectId of Event of which repetition requested
26-
* @param {string|null} [repetitionId] - if not specified, last repetition will returned
27-
* @return {Promise<EventRepetitionSchema>}
28-
*/
29-
async repetition({ id: eventId, projectId }, { id: repetitionId }) {
30-
const factory = new EventsFactory(projectId);
31-
32-
if (!repetitionId) {
33-
return factory.getEventLastRepetition(eventId);
34-
}
35-
36-
return factory.getEventRepetition(repetitionId);
37-
},
3821

3922
/**
40-
* Returns repetitions list of the event
23+
* Returns repetitions portion of the event
4124
*
4225
* @param {ResolverObj} _obj
4326
* @param {String} eventId
4427
* @param {String} projectId
4528
* @param {Number} limit
46-
* @param {Number} skip
29+
* @param {Number} cursor
4730
*
48-
* @return {EventRepetitionSchema[]}
31+
* @return {RepetitionsPortion}
4932
*/
50-
async repetitions({ _id: eventId, projectId }, { limit, skip }) {
33+
async repetitionsPortion({ _id: eventId, projectId }, { limit, cursor }) {
5134
const factory = new EventsFactory(projectId);
5235

53-
return factory.getEventRepetitions(eventId, limit, skip);
36+
return factory.getEventRepetitions(eventId, limit, cursor);
5437
},
5538

5639
/**

src/resolvers/project.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -287,17 +287,17 @@ module.exports = {
287287
*
288288
* @returns {Event}
289289
*/
290-
async event(project, { id: eventId }) {
290+
async event(project, { id: repetitionId }) {
291291
const factory = new EventsFactory(project._id);
292-
const event = await factory.findById(eventId);
292+
const repetition = await factory.getEventRepetition(repetitionId);
293293

294-
if (!event) {
294+
if (!repetition) {
295295
return null;
296296
}
297297

298-
event.projectId = project._id;
298+
repetition.projectId = project._id;
299299

300-
return event;
300+
return repetition;
301301
},
302302

303303
/**

0 commit comments

Comments
 (0)