@@ -6,6 +6,7 @@ const Factory = require('./modelFactory');
66const mongo = require ( '../mongo' ) ;
77const Event = require ( '../models/event' ) ;
88const { ObjectID } = require ( 'mongodb' ) ;
9+ const { composeEventPayloadWithRepetition } = require ( '../utils/merge' ) ;
910
1011/**
1112 * @typedef {Object } EventRepetitionSchema
@@ -403,23 +404,33 @@ class EventsFactory extends Factory {
403404 /**
404405 * Returns Event repetitions
405406 *
406- * @param {string|ObjectID } eventId - Event's id
407+ * @param {string|ObjectID } eventId - Event's id (may be repetition id)
407408 * @param {Number } limit - count limitations
408- * @param {Number } skip - selection offset
409+ * @param {Number } cursor - pointer to the next repetition
409410 *
410411 * @return {EventRepetitionSchema[] }
411412 *
412413 * @todo move to Repetitions(?) model
413414 */
414- async getEventRepetitions ( eventId , limit = 10 , skip = 0 ) {
415+ async getEventRepetitions ( eventId , limit = 10 , cursor = null ) {
415416 limit = this . validateLimit ( limit ) ;
416- skip = this . validateSkip ( skip ) ;
417+
418+ cursor = cursor ? new ObjectID ( cursor ) : null ;
419+
420+ const result = {
421+ repetitions : [ ] ,
422+ nextCursor : null ,
423+ } ;
417424
418425 /**
419426 * Get original event
420427 * @type {EventSchema }
421428 */
422- const eventOriginal = await this . findById ( eventId ) ;
429+ const eventOriginal = await this . _findOriginalEvent ( eventId ) ;
430+
431+ if ( ! eventOriginal ) {
432+ return result ;
433+ }
423434
424435 /**
425436 * Collect repetitions
@@ -428,13 +439,26 @@ class EventsFactory extends Factory {
428439 const repetitions = await this . getCollection ( this . TYPES . REPETITIONS )
429440 . find ( {
430441 groupHash : eventOriginal . groupHash ,
442+ _id : cursor ? { $lte : cursor } : { $exists : true } ,
431443 } )
432444 . sort ( { _id : - 1 } )
433- . limit ( limit )
434- . skip ( skip )
445+ . limit ( limit + 1 )
435446 . toArray ( ) ;
436447
437- const isLastPortion = repetitions . length < limit && skip === 0 ;
448+ if ( repetitions . length === limit + 1 ) {
449+ result . nextCursor = repetitions . pop ( ) . _id ;
450+ }
451+
452+ for ( const repetition of repetitions ) {
453+ const event = this . _composeEventWithRepetition ( eventOriginal , repetition ) ;
454+
455+ result . repetitions . push ( {
456+ ...event ,
457+ projectId : this . projectId ,
458+ } ) ;
459+ }
460+
461+ const isLastPortion = result . nextCursor === null ;
438462
439463 /**
440464 * For last portion:
@@ -446,16 +470,15 @@ class EventsFactory extends Factory {
446470 * @type {EventRepetitionSchema }
447471 */
448472 const firstRepetition = {
449- _id : eventOriginal . _id ,
450- payload : eventOriginal . payload ,
451- groupHash : eventOriginal . groupHash ,
452- timestamp : eventOriginal . timestamp ,
473+ ...eventOriginal ,
474+ firstAppearanceTimestamp : eventOriginal . timestamp ,
475+ projectId : this . projectId ,
453476 } ;
454477
455- repetitions . push ( firstRepetition ) ;
478+ result . repetitions . push ( firstRepetition ) ;
456479 }
457480
458- return repetitions ;
481+ return result ;
459482 }
460483
461484 /**
@@ -467,10 +490,33 @@ class EventsFactory extends Factory {
467490 * @todo move to Repetitions(?) model
468491 */
469492 async getEventRepetition ( repetitionId ) {
470- return this . getCollection ( this . TYPES . REPETITIONS )
493+ const repetition = await this . getCollection ( this . TYPES . REPETITIONS )
471494 . findOne ( {
472495 _id : ObjectID ( repetitionId ) ,
473496 } ) ;
497+
498+ if ( ! repetition ) {
499+ /**
500+ * If repetition is not found, it can mean that client is trying to get original event
501+ */
502+ const event = await this . findById ( repetitionId ) ;
503+
504+ return event ? {
505+ ...event ,
506+ firstAppearanceTimestamp : event . timestamp ,
507+ } : null ;
508+ }
509+
510+ const originalEvent = await this . getCollection ( this . TYPES . EVENTS )
511+ . findOne ( {
512+ groupHash : repetition . groupHash ,
513+ } ) ;
514+
515+ if ( ! originalEvent ) {
516+ return null ;
517+ }
518+
519+ return this . _composeEventWithRepetition ( originalEvent , repetition ) ;
474520 }
475521
476522 /**
@@ -495,7 +541,11 @@ class EventsFactory extends Factory {
495541 * @returns {Release|null }
496542 */
497543 async getEventRelease ( eventId ) {
498- const eventOriginal = await this . findById ( eventId ) ;
544+ const eventOriginal = await this . _findOriginalEvent ( eventId ) ;
545+
546+ if ( ! eventOriginal ) {
547+ return null ;
548+ }
499549
500550 const release = await mongo . databases . events . collection ( this . TYPES . RELEASES ) . findOne ( {
501551 release : eventOriginal . payload . release ,
@@ -514,9 +564,15 @@ class EventsFactory extends Factory {
514564 * @return {Promise<void> }
515565 */
516566 async visitEvent ( eventId , userId ) {
567+ const event = await this . _findOriginalEvent ( eventId ) ;
568+
569+ if ( ! event ) {
570+ return null ;
571+ }
572+
517573 return this . getCollection ( this . TYPES . EVENTS )
518574 . updateOne (
519- { _id : new ObjectID ( eventId ) } ,
575+ { _id : new ObjectID ( event . _id ) } ,
520576 { $addToSet : { visitedBy : new ObjectID ( userId ) } }
521577 ) ;
522578 }
@@ -531,9 +587,15 @@ class EventsFactory extends Factory {
531587 */
532588 async toggleEventMark ( eventId , mark ) {
533589 const collection = this . getCollection ( this . TYPES . EVENTS ) ;
534- const query = { _id : new ObjectID ( eventId ) } ;
535590
536- const event = await collection . findOne ( query ) ;
591+ const event = await this . _findOriginalEvent ( eventId ) ;
592+
593+ if ( ! event ) {
594+ return null ;
595+ }
596+
597+ const query = { _id : new ObjectID ( event . _id ) } ;
598+
537599 const markKey = `marks.${ mark } ` ;
538600
539601 let update ;
@@ -583,13 +645,74 @@ class EventsFactory extends Factory {
583645 */
584646 async updateAssignee ( eventId , assignee ) {
585647 const collection = this . getCollection ( this . TYPES . EVENTS ) ;
586- const query = { _id : new ObjectID ( eventId ) } ;
648+
649+ const event = await this . _findOriginalEvent ( eventId ) ;
650+
651+ if ( ! event ) {
652+ return null ;
653+ }
654+
655+ const query = { _id : new ObjectID ( event . _id ) } ;
656+
587657 const update = {
588658 $set : { assignee : assignee } ,
589659 } ;
590660
591661 return collection . updateOne ( query , update ) ;
592662 }
663+
664+ /**
665+ * Find original event by eventId. If event is not found directly,
666+ * try to find it as repetition and get original event by groupHash
667+ *
668+ * @param {string|ObjectID } eventId - event's id, may be repetition id
669+ * @returns {Promise<Event|null> } original event or null if not found
670+ */
671+ async _findOriginalEvent ( eventId ) {
672+ let originalEvent ;
673+
674+ /**
675+ * Try to find it by repetitionId
676+ */
677+ const repetition = await this . getCollection ( this . TYPES . REPETITIONS )
678+ . findOne ( {
679+ _id : new ObjectID ( eventId ) ,
680+ } ) ;
681+
682+ /**
683+ * If repetition is not found by eventId, try to find it by eventId
684+ */
685+ if ( ! repetition ) {
686+ originalEvent = await this . getCollection ( this . TYPES . EVENTS )
687+ . findOne ( {
688+ _id : new ObjectID ( eventId ) ,
689+ } ) ;
690+ } else {
691+ originalEvent = await this . getCollection ( this . TYPES . EVENTS )
692+ . findOne ( {
693+ groupHash : repetition . groupHash ,
694+ } ) ;
695+ }
696+
697+ return originalEvent ;
698+ }
699+
700+ /**
701+ * Compose event with repetition
702+ *
703+ * @param {Event } event - event
704+ * @param {Repetition } repetition - repetition
705+ * @returns {Event } event merged with repetition
706+ */
707+ _composeEventWithRepetition ( event , repetition ) {
708+ return {
709+ ...event ,
710+ _id : repetition . _id ,
711+ firstAppearanceTimestamp : event . timestamp ,
712+ timestamp : repetition . timestamp ,
713+ payload : composeEventPayloadWithRepetition ( event . payload , repetition ) ,
714+ } ;
715+ }
593716}
594717
595718module . exports = EventsFactory ;
0 commit comments