Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 1 addition & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "hawk.api",
"version": "1.1.32",
"version": "1.1.33",
"main": "index.ts",
"license": "UNLICENSED",
"scripts": {
Expand Down Expand Up @@ -44,8 +44,6 @@
"@types/debug": "^4.1.5",
"@types/escape-html": "^1.0.0",
"@types/graphql-upload": "^8.0.11",
"@types/lodash.clonedeep": "^4.5.9",
"@types/lodash.mergewith": "^4.6.9",
"@types/jsonwebtoken": "^8.3.5",
"@types/lodash.clonedeep": "^4.5.9",
"@types/lodash.mergewith": "^4.6.9",
Expand Down
164 changes: 144 additions & 20 deletions src/models/eventsFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const Factory = require('./modelFactory');
const mongo = require('../mongo');
const Event = require('../models/event');
const { ObjectID } = require('mongodb');
const { composeEventPayloadWithRepetition } = require('../utils/merge');

/**
* @typedef {Object} RecentEventSchema
Expand Down Expand Up @@ -287,6 +288,7 @@ class EventsFactory extends Factory {
if (result && result.events) {
result.events.forEach(event => {
event.projectId = this.projectId;
event.firstAppearanceTimestamp = event.timestamp;
});
}

Expand Down Expand Up @@ -391,23 +393,33 @@ class EventsFactory extends Factory {
/**
* Returns Event repetitions
*
* @param {string|ObjectID} eventId - Event's id
* @param {string|ObjectID} eventId - Event's id (may be repetition id)
* @param {Number} limit - count limitations
* @param {Number} skip - selection offset
* @param {Number} cursor - pointer to the next repetition
*
* @return {EventRepetitionSchema[]}
*
* @todo move to Repetitions(?) model
*/
async getEventRepetitions(eventId, limit = 10, skip = 0) {
async getEventRepetitions(eventId, limit = 10, cursor = null) {
limit = this.validateLimit(limit);
skip = this.validateSkip(skip);

cursor = cursor ? new ObjectID(cursor) : null;

const result = {
repetitions: [],
nextCursor: null,
};

/**
* Get original event
* @type {EventSchema}
*/
const eventOriginal = await this.findById(eventId);
const eventOriginal = await this._findOriginalEvent(eventId);

if (!eventOriginal) {
return result;
}

/**
* Collect repetitions
Expand All @@ -416,13 +428,26 @@ class EventsFactory extends Factory {
const repetitions = await this.getCollection(this.TYPES.REPETITIONS)
.find({
groupHash: eventOriginal.groupHash,
_id: cursor ? { $lte: cursor } : { $exists: true },
})
.sort({ _id: -1 })
.limit(limit)
.skip(skip)
.limit(limit + 1)
.toArray();

const isLastPortion = repetitions.length < limit && skip === 0;
if (repetitions.length === limit + 1) {
result.nextCursor = repetitions.pop()._id;
}

for (const repetition of repetitions) {
const event = this._composeEventWithRepetition(eventOriginal, repetition);

result.repetitions.push({
...event,
projectId: this.projectId,
});
}

const isLastPortion = result.nextCursor === null;

/**
* For last portion:
Expand All @@ -434,16 +459,15 @@ class EventsFactory extends Factory {
* @type {EventRepetitionSchema}
*/
const firstRepetition = {
_id: eventOriginal._id,
payload: eventOriginal.payload,
groupHash: eventOriginal.groupHash,
timestamp: eventOriginal.timestamp,
...eventOriginal,
firstAppearanceTimestamp: eventOriginal.timestamp,
projectId: this.projectId,
};

repetitions.push(firstRepetition);
result.repetitions.push(firstRepetition);
}

return repetitions;
return result;
}

/**
Expand All @@ -455,10 +479,33 @@ class EventsFactory extends Factory {
* @todo move to Repetitions(?) model
*/
async getEventRepetition(repetitionId) {
return this.getCollection(this.TYPES.REPETITIONS)
const repetition = await this.getCollection(this.TYPES.REPETITIONS)
.findOne({
_id: ObjectID(repetitionId),
});

if (!repetition) {
/**
* If repetition is not found, it can mean that client is trying to get original event
*/
const event = await this.findById(repetitionId);

return event ? {
...event,
firstAppearanceTimestamp: event.timestamp,
} : null;
}

const originalEvent = await this.getCollection(this.TYPES.EVENTS)
.findOne({
groupHash: repetition.groupHash,
});

if (!originalEvent) {
return null;
}

return this._composeEventWithRepetition(originalEvent, repetition);
}

/**
Expand All @@ -483,7 +530,11 @@ class EventsFactory extends Factory {
* @returns {Release|null}
*/
async getEventRelease(eventId) {
const eventOriginal = await this.findById(eventId);
const eventOriginal = await this._findOriginalEvent(eventId);

if (!eventOriginal) {
return null;
}

const release = await mongo.databases.events.collection(this.TYPES.RELEASES).findOne({
release: eventOriginal.payload.release,
Expand All @@ -502,9 +553,15 @@ class EventsFactory extends Factory {
* @return {Promise<void>}
*/
async visitEvent(eventId, userId) {
const event = await this._findOriginalEvent(eventId);

if (!event) {
return null;
}

return this.getCollection(this.TYPES.EVENTS)
.updateOne(
{ _id: new ObjectID(eventId) },
{ _id: new ObjectID(event._id) },
{ $addToSet: { visitedBy: new ObjectID(userId) } }
);
}
Expand All @@ -519,9 +576,15 @@ class EventsFactory extends Factory {
*/
async toggleEventMark(eventId, mark) {
const collection = this.getCollection(this.TYPES.EVENTS);
const query = { _id: new ObjectID(eventId) };

const event = await collection.findOne(query);
const event = await this._findOriginalEvent(eventId);

if (!event) {
return null;
}

const query = { _id: new ObjectID(event._id) };

const markKey = `marks.${mark}`;

let update;
Expand Down Expand Up @@ -571,13 +634,74 @@ class EventsFactory extends Factory {
*/
async updateAssignee(eventId, assignee) {
const collection = this.getCollection(this.TYPES.EVENTS);
const query = { _id: new ObjectID(eventId) };

const event = await this._findOriginalEvent(eventId);

if (!event) {
return null;
}

const query = { _id: new ObjectID(event._id) };

const update = {
$set: { assignee: assignee },
};

return collection.updateOne(query, update);
}

/**
* Find original event by eventId. If event is not found directly,
* try to find it as repetition and get original event by groupHash
*
* @param {string|ObjectID} eventId - event's id, may be repetition id
* @returns {Promise<Event|null>} original event or null if not found
*/
async _findOriginalEvent(eventId) {
let originalEvent;

/**
* Try to find it by repetitionId
*/
const repetition = await this.getCollection(this.TYPES.REPETITIONS)
.findOne({
_id: new ObjectID(eventId),
});

/**
* If repetition is not found by eventId, try to find it by eventId
*/
if (!repetition) {
originalEvent = await this.getCollection(this.TYPES.EVENTS)
.findOne({
_id: new ObjectID(eventId),
});
} else {
originalEvent = await this.getCollection(this.TYPES.EVENTS)
.findOne({
groupHash: repetition.groupHash,
});
}

return originalEvent;
}

/**
* Compose event with repetition
*
* @param {Event} event - event
* @param {Repetition} repetition - repetition
* @returns {Event} event merged with repetition
*/
_composeEventWithRepetition(event, repetition) {
return {
...event,
_id: repetition._id,
firstAppearanceTimestamp: event.timestamp,
timestamp: repetition.timestamp,
payload: composeEventPayloadWithRepetition(event.payload, repetition),
};
}
}

module.exports = EventsFactory;
27 changes: 5 additions & 22 deletions src/resolvers/event.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,39 +18,22 @@ module.exports = {
},
},
Event: {
/**
* Returns Event with concrete repetition
*
* @param {string} eventId - id of Event of which repetition requested
* @param {string} projectId - projectId of Event of which repetition requested
* @param {string|null} [repetitionId] - if not specified, last repetition will returned
* @return {Promise<EventRepetitionSchema>}
*/
async repetition({ id: eventId, projectId }, { id: repetitionId }) {
const factory = new EventsFactory(projectId);

if (!repetitionId) {
return factory.getEventLastRepetition(eventId);
}

return factory.getEventRepetition(repetitionId);
},

/**
* Returns repetitions list of the event
* Returns repetitions portion of the event
*
* @param {ResolverObj} _obj
* @param {String} eventId
* @param {String} projectId
* @param {Number} limit
* @param {Number} skip
* @param {Number} cursor
*
* @return {EventRepetitionSchema[]}
* @return {RepetitionsPortion}
*/
async repetitions({ _id: eventId, projectId }, { limit, skip }) {
async repetitionsPortion({ _id: eventId, projectId }, { limit, cursor }) {
const factory = new EventsFactory(projectId);

return factory.getEventRepetitions(eventId, limit, skip);
return factory.getEventRepetitions(eventId, limit, cursor);
},

/**
Expand Down
10 changes: 5 additions & 5 deletions src/resolvers/project.js
Original file line number Diff line number Diff line change
Expand Up @@ -287,17 +287,17 @@ module.exports = {
*
* @returns {Event}
*/
async event(project, { id: eventId }) {
async event(project, { id: repetitionId }) {
const factory = new EventsFactory(project._id);
const event = await factory.findById(eventId);
const repetition = await factory.getEventRepetition(repetitionId);

if (!event) {
if (!repetition) {
return null;
}

event.projectId = project._id;
repetition.projectId = project._id;

return event;
return repetition;
},

/**
Expand Down
Loading
Loading