Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
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.0.6",
"version": "1.0.7",
"main": "index.ts",
"license": "UNLICENSED",
"scripts": {
Expand Down
91 changes: 76 additions & 15 deletions src/models/eventsFactory.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { getMidnightWithTimezoneOffset, getUTCMidnight } from '../utils/dates';
import { groupBy } from '../utils/grouper';

const Factory = require('./modelFactory');
Expand Down Expand Up @@ -258,24 +257,29 @@ class EventsFactory extends Factory {
}

/**
* Fetch timestamps and total count of errors (or target error) for each day since
* Get daily events for last few days grouped by user timezone days
*
* @param {number} days - how many days we need to fetch for displaying in a chart
* @param {number} timezoneOffset - user's local timezone offset in minutes
* @param {string} groupHash - event's group hash for showing only target event
* @return {ProjectChartItem[]}
* @param {number} days - days to get
* @param {number} [timezoneOffset] - user timezone offset in minutes
* @param {string} [groupHash] - event group hash
* @return {Promise<object>}
*/
async findChartData(days, timezoneOffset = 0, groupHash = '') {
async getGroupedDailyEvents(days, timezoneOffset = 0, groupHash) {
const today = new Date();
const since = today.setDate(today.getDate() - days) / 1000;

today.setHours(0, 0, 0, 0);

const since = new Date();

since.setTime(today.getTime() + timezoneOffset * 60 * 1000 - days * 24 * 60 * 60 * 1000);

/**
* Compose options for find method
* @type {{groupingTimestamp: {$gt: number}}}
*/
const options = {
groupingTimestamp: {
$gt: since,
$gt: since / 1000,
},
};

Expand All @@ -296,16 +300,32 @@ class EventsFactory extends Factory {
* Convert UTC midnight to midnights in user's timezone
*/
dailyEvents = dailyEvents.map((item) => {
const groupingTimestamp = new Date(item.groupingTimestamp * 1000);

groupingTimestamp.setTime(groupingTimestamp.getTime() + timezoneOffset * 60 * 1000);
Copy link
Member

Choose a reason for hiding this comment

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

why the getMidnightWithTimezoneOffset util is replaced with such a line?

Copy link
Member Author

Choose a reason for hiding this comment

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

Because it didn't work as expected and is too complicated.

groupingTimestamp is already UTC midnight, so we just need to recalculate it with timezone offset

Copy link
Member

Choose a reason for hiding this comment

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

looks suspicious. It was working fine and handled edge points with errors that happened near midnight.

Why do you add a timezoneOffset to the Midnight? It won't be midnight after that.

As I can see, the getMidnightWithTimezoneOffset has a comment describing a problem. What exactly does not work as expected?


return Object.assign({}, item, {
groupingTimestamp: getMidnightWithTimezoneOffset(item.lastRepetitionTime, item.groupingTimestamp, timezoneOffset),
groupingTimestamp: groupingTimestamp / 1000,
});
});

/**
* Group events using 'groupByTimestamp:NNNNNNNN' key
* @type {ProjectChartItem[]}
*/
const groupedData = groupBy('groupingTimestamp')(dailyEvents);
return groupBy('groupingTimestamp')(dailyEvents);
}

/**
* Fetch timestamps and count of affected users for each day since
*
* @param {number} days - how many days we need to fetch for displaying in a chart
* @param {number} timezoneOffset - user's local timezone offset in minutes
* @param {string} groupHash - event's group hash for showing only target event
* @return {AffectedUsersChartItem[]}
*/
async findAffectedUsersChart(days, timezoneOffset = 0, groupHash = '') {
const groupedData = await this.getGroupedDailyEvents(days, timezoneOffset, groupHash);

/**
* Now fill all requested days
Expand All @@ -314,12 +334,53 @@ class EventsFactory extends Factory {

for (let i = 0; i < days; i++) {
const now = new Date();
const day = new Date(now.setDate(now.getDate() - i));
const dayMidnight = getUTCMidnight(day) / 1000;
const groupedEvents = groupedData[`groupingTimestamp:${dayMidnight}`];

now.setHours(0, 0, 0, 0);
Copy link
Member

Choose a reason for hiding this comment

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

why to not use getUTCMidnight instead?

Copy link
Member Author

Choose a reason for hiding this comment

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

getUTCMidnight sounds like a pure function but in fact it implicitly change passed date. So I think explicit set is better here

Copy link
Member

Choose a reason for hiding this comment

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

so you can rename it with setUTCMidnight. Using a separate method will improve the readability of this code

now.setTime(now.getTime() + timezoneOffset * 60 * 1000 - i * 24 * 60 * 60 * 1000);

const groupedEvents = groupedData[`groupingTimestamp:${now / 1000}`];
const affectedUsers = groupedEvents ? groupedEvents.reduce((set, value) => new Set([...set, ...value.affectedUsers]), new Set()) : new Set();

result.push({
timestamp: now / 1000,
count: affectedUsers.size,
});
}

/**
* Order by time ascendance
*/
result = result.sort((a, b) => a.timestamp - b.timestamp);

return result;
}

/**
* Fetch timestamps and total count of errors (or target error) for each day since
*
* @param {number} days - how many days we need to fetch for displaying in a chart
* @param {number} timezoneOffset - user's local timezone offset in minutes
* @param {string} groupHash - event's group hash for showing only target event
* @return {ProjectChartItem[]}
*/
async findChartData(days, timezoneOffset = 0, groupHash = '') {
const groupedData = await this.getGroupedDailyEvents(days, timezoneOffset, groupHash);

/**
* Now fill all requested days
*/
let result = [];

for (let i = 0; i < days; i++) {
const now = new Date();

now.setHours(0, 0, 0, 0);
now.setTime(now.getTime() + timezoneOffset * 60 * 1000 - i * 24 * 60 * 60 * 1000);

const groupedEvents = groupedData[`groupingTimestamp:${now / 1000}`];

result.push({
timestamp: dayMidnight,
timestamp: now / 1000,
count: groupedEvents ? groupedEvents.reduce((sum, value) => sum + value.count, 0) : 0,
});
}
Expand Down
15 changes: 15 additions & 0 deletions src/resolvers/event.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,21 @@ module.exports = {
return factories.usersFactory.dataLoaders.userById.load(assignee);
},

/**
* Return chart data for target event affected users in last few days
*
* @param {string} projectId - event's project
* @param {string} groupHash - event's groupHash
* @param {number} days - how many days we need to fetch for displaying in a charts
* @param {number} timezoneOffset - user's local timezone offset in minutes
* @returns {Promise<ProjectChartItem[]>}
*/
async usersAffectedChart({ projectId, groupHash }, { days, timezoneOffset }) {
const factory = new EventsFactory(new ObjectID(projectId));

return factory.findAffectedUsersChart(days, timezoneOffset, groupHash);
},

/**
* Return chart data for target event occured in last few days
*
Expand Down
21 changes: 21 additions & 0 deletions src/typeDefs/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,21 @@ type Event {
"""
usersAffected: Int

"""
Return affected users chart data for the last few days
"""
usersAffectedChart(
"""
How many days we need to fetch for displaying in a chart
"""
days: Int! = 0

"""
User's local timezone offset in minutes
"""
timezoneOffset: Int! = 0
): [ChartDataItem!]! @requireAuth

"""
Return graph of the error rate for the last few days
"""
Expand Down Expand Up @@ -392,6 +407,12 @@ type DailyEventInfo {
Last event occurrence timestamp
"""
lastRepetitionTime: Float!


"""
Array of user's ids affected this day
"""
affectedUsers: [String!]
}

type Subscription {
Expand Down