From 594978848937a8bbeb2ef018a1725d11dbc341ab Mon Sep 17 00:00:00 2001 From: slaveeks Date: Wed, 21 Jan 2026 19:56:41 +0300 Subject: [PATCH 1/4] feat(affected-users): added chart data --- src/models/eventsFactory.js | 43 +++++++++++++++++++++++++++++-------- src/resolvers/event.js | 15 +++++++++++++ src/sso/saml/controller.ts | 5 +++++ src/typeDefs/event.ts | 15 +++++++++++++ 4 files changed, 69 insertions(+), 9 deletions(-) diff --git a/src/models/eventsFactory.js b/src/models/eventsFactory.js index bf3efb13..81ab4559 100644 --- a/src/models/eventsFactory.js +++ b/src/models/eventsFactory.js @@ -18,6 +18,7 @@ const MAX_DB_READ_BATCH_SIZE = Number(process.env.MAX_DB_READ_BATCH_SIZE); const ChartType = { Accepted: 'accepted', RateLimited: 'rate-limited', + AffectedUsers: 'affected-users', }; /** @@ -523,15 +524,35 @@ class EventsFactory extends Factory { ]; } + /** + * Get event daily chart data (affected users) from MongoDB only + * + * @param {string} groupHash - event's group hash + * @param {number} days - how many days to fetch + * @param {number} timezoneOffset - user's local timezone offset in minutes + * @returns {Promise} + */ + async getEventDailyAffectedUsersChart(groupHash, days, timezoneOffset = 0) { + const data = await this.findChartData(days, timezoneOffset, groupHash, 'affectedUsers'); + + return [ + { + label: ChartType.AffectedUsers, + data, + }, + ]; + } + /** * 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 + * @param {'count'|'affectedUsers'} valueField - field to aggregate * @return {ProjectChartItem[]} */ - async findChartData(days, timezoneOffset = 0, groupHash = '') { + async findChartData(days, timezoneOffset = 0, groupHash = '', valueField = 'count') { const today = new Date(); const since = today.setDate(today.getDate() - days) / 1000; @@ -554,13 +575,15 @@ class EventsFactory extends Factory { }; } + const projection = { + lastRepetitionTime: 1, + groupingTimestamp: 1, + [valueField]: 1, + }; + const dailyEventsCursor = await this.getCollection(this.TYPES.DAILY_EVENTS) .find(options, { - projection: { - lastRepetitionTime: 1, - groupingTimestamp: 1, - count: 1, - }, + projection, }) .batchSize(MAX_DB_READ_BATCH_SIZE); @@ -576,11 +599,13 @@ class EventsFactory extends Factory { const key = `groupingTimestamp:${groupingTimestamp}`; const current = groupedCounts[key] || 0; - if (item.count === undefined || item.count === null) { - console.warn(`Missing 'count' field for daily event with key ${key}. Defaulting to 0.`); + const value = item[valueField]; + + if (value === undefined || value === null) { + console.warn(`Missing '${valueField}' field for daily event with key ${key}. Defaulting to 0.`); groupedCounts[key] = current; } else { - groupedCounts[key] = current + item.count; + groupedCounts[key] = current + value; } } diff --git a/src/resolvers/event.js b/src/resolvers/event.js index c3c44971..b0b49eb5 100644 --- a/src/resolvers/event.js +++ b/src/resolvers/event.js @@ -90,6 +90,21 @@ module.exports = { return factory.getEventDailyChart(groupHash, days, timezoneOffset); }, + /** + * Return chart data for affected users of target event occured 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} + */ + async affectedUsersChartData({ projectId, groupHash }, { days, timezoneOffset }, context) { + const factory = getEventsFactory(context, projectId); + + return factory.getEventDailyAffectedUsersChart(groupHash, days, timezoneOffset); + }, + /** * Return AI suggestion for the event * diff --git a/src/sso/saml/controller.ts b/src/sso/saml/controller.ts index 399d1c28..11d01d5d 100644 --- a/src/sso/saml/controller.ts +++ b/src/sso/saml/controller.ts @@ -154,6 +154,7 @@ export default class SamlController { res.redirect(redirectUrl.toString()); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; + this.log( 'error', 'SSO initiation error for workspace:', @@ -271,6 +272,7 @@ export default class SamlController { if (!isValidRequest) { const requestIdShort = samlData.inResponseTo.slice(0, 8); + this.log( 'error', '[ACS] InResponseTo validation failed for workspace:', @@ -285,6 +287,7 @@ export default class SamlController { } } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; + this.log( 'error', '[ACS] SAML validation error for workspace:', @@ -387,6 +390,7 @@ export default class SamlController { */ if (error instanceof Error && error.message.includes('SAML')) { const errorMessage = error.message; + this.log( 'error', '[ACS] SAML processing error for workspace:', @@ -400,6 +404,7 @@ export default class SamlController { } const errorMessage = error instanceof Error ? error.message : 'Unknown error'; + this.log( 'error', '[ACS] ACS callback error for workspace:', diff --git a/src/typeDefs/event.ts b/src/typeDefs/event.ts index a82b6762..b87d13a9 100644 --- a/src/typeDefs/event.ts +++ b/src/typeDefs/event.ts @@ -296,6 +296,21 @@ type Event { """ timezoneOffset: Int! = 0 ): [ChartLine!]! + + """ + Return graph of affected users dynamics for the specified period + """ + affectedUsersChartData( + """ + 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 + ): [ChartLine!]! } """ From 9fd3d258269ae84e99f3579cb6b874e340468173 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 17:00:46 +0000 Subject: [PATCH 2/4] Bump version up to 1.3.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a0f7db79..279c401f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hawk.api", - "version": "1.3.1", + "version": "1.3.2", "main": "index.ts", "license": "BUSL-1.1", "scripts": { From cd49dd490753d6c93bd5080fa04ffff389f14a33 Mon Sep 17 00:00:00 2001 From: slaveeks Date: Wed, 21 Jan 2026 20:02:15 +0300 Subject: [PATCH 3/4] for bump --- src/models/eventsFactory.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/eventsFactory.js b/src/models/eventsFactory.js index 81ab4559..eace8fab 100644 --- a/src/models/eventsFactory.js +++ b/src/models/eventsFactory.js @@ -525,7 +525,7 @@ class EventsFactory extends Factory { } /** - * Get event daily chart data (affected users) from MongoDB only + * Get event daily chart data of affected users from MongoDB only * * @param {string} groupHash - event's group hash * @param {number} days - how many days to fetch From ae92c3ab36151626e7515b1d7e7376f311016ea3 Mon Sep 17 00:00:00 2001 From: Vyacheslav Chernyshev <81693471+slaveeks@users.noreply.github.com> Date: Wed, 21 Jan 2026 20:50:36 +0300 Subject: [PATCH 4/4] Update src/models/eventsFactory.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/models/eventsFactory.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/eventsFactory.js b/src/models/eventsFactory.js index eace8fab..0b911bf6 100644 --- a/src/models/eventsFactory.js +++ b/src/models/eventsFactory.js @@ -544,7 +544,7 @@ class EventsFactory extends Factory { } /** - * Fetch timestamps and total count of errors (or target error) for each day since + * Fetch timestamps and aggregated values (count or affectedUsers) 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