Skip to content

Commit c3fd9df

Browse files
committed
Expose rate-limited series alongside accepted chart
1 parent 5907e7a commit c3fd9df

5 files changed

Lines changed: 85 additions & 15 deletions

File tree

src/models/eventsFactory.js

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -443,25 +443,52 @@ class EventsFactory extends Factory {
443443
const days = Math.ceil((end - start) / (24 * 60 * 60 * 1000));
444444

445445
try {
446-
const redisData = await this.chartDataService.getProjectChartData(
447-
projectId,
448-
startDate,
449-
endDate,
450-
groupBy,
451-
timezoneOffset
452-
);
453-
454-
if (redisData && redisData.length > 0) {
455-
return redisData;
446+
const [acceptedSeries, rateLimitedSeries] = await Promise.all([
447+
this.chartDataService.getProjectChartData(
448+
projectId,
449+
startDate,
450+
endDate,
451+
groupBy,
452+
timezoneOffset,
453+
'events-accepted'
454+
),
455+
this.chartDataService.getProjectChartData(
456+
projectId,
457+
startDate,
458+
endDate,
459+
groupBy,
460+
timezoneOffset,
461+
'events-rate-limited'
462+
),
463+
]);
464+
465+
const hasAccepted = Array.isArray(acceptedSeries) && acceptedSeries.length > 0;
466+
const hasRateLimited = Array.isArray(rateLimitedSeries) && rateLimitedSeries.length > 0;
467+
468+
if (hasAccepted || hasRateLimited) {
469+
return {
470+
accepted: acceptedSeries,
471+
rateLimited: hasRateLimited
472+
? rateLimitedSeries
473+
: this._composeZeroSeries(acceptedSeries),
474+
};
456475
}
457476

458477
// Fallback to Mongo (empty groupHash for project-level data)
459-
return this.findChartData(days, timezoneOffset, '');
478+
const fallbackAccepted = await this.findChartData(days, timezoneOffset, '');
479+
480+
return {
481+
accepted: fallbackAccepted,
482+
};
460483
} catch (err) {
461484
console.error('[EventsFactory] getProjectChartData error:', err);
462485

463486
// Fallback to Mongo on error (empty groupHash for project-level data)
464-
return this.findChartData(days, timezoneOffset, '');
487+
const fallbackAccepted = await this.findChartData(days, timezoneOffset, '');
488+
489+
return {
490+
accepted: fallbackAccepted,
491+
};
465492
}
466493
}
467494

@@ -568,6 +595,23 @@ class EventsFactory extends Factory {
568595
return result;
569596
}
570597

598+
/**
599+
* Compose zero-filled chart series using timestamps from the provided template
600+
*
601+
* @param {Array<{timestamp: number, count: number}>} template - reference series for timestamps
602+
* @returns {Array<{timestamp: number, count: number}>}
603+
*/
604+
_composeZeroSeries(template = []) {
605+
if (!Array.isArray(template) || template.length === 0) {
606+
return [];
607+
}
608+
609+
return template.map((point) => ({
610+
timestamp: point.timestamp,
611+
count: 0,
612+
}));
613+
}
614+
571615
/**
572616
* Returns number of documents that occurred after the last visit time
573617
*

src/redisHelper.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export default class RedisHelper {
3636
constructor() {
3737
if (!process.env.REDIS_URL) {
3838
console.warn('[Redis] REDIS_URL not set, Redis features will be disabled');
39+
3940
return;
4041
}
4142

@@ -49,7 +50,9 @@ export default class RedisHelper {
4950
* Max wait time: 30 seconds
5051
*/
5152
const delay = Math.min(retries * 1000, 30000);
53+
5254
console.log(`[Redis] Reconnecting... attempt ${retries}, waiting ${delay}ms`);
55+
5356
return delay;
5457
},
5558
},
@@ -93,6 +96,7 @@ export default class RedisHelper {
9396
if (!RedisHelper.instance) {
9497
RedisHelper.instance = new RedisHelper();
9598
}
99+
96100
return RedisHelper.instance;
97101
}
98102

@@ -102,6 +106,7 @@ export default class RedisHelper {
102106
public async initialize(): Promise<void> {
103107
if (!this.redisClient) {
104108
console.warn('[Redis] Client not initialized, skipping connection');
109+
105110
return;
106111
}
107112

src/services/chartDataService.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export default class ChartDataService {
1919
* @param endDate - end date as ISO string (e.g., '2025-01-31T23:59:59Z')
2020
* @param groupBy - grouping interval in minutes (1=minute, 60=hour, 1440=day)
2121
* @param timezoneOffset - user's local timezone offset in minutes (default: 0)
22+
* @param metricType - Redis metric type suffix (e.g., 'events-accepted', 'events-rate-limited')
2223
* @returns Array of data points with timestamp and count
2324
* @throws Error if Redis is not connected (caller should fallback to MongoDB)
2425
*/
@@ -27,7 +28,8 @@ export default class ChartDataService {
2728
startDate: string,
2829
endDate: string,
2930
groupBy: number,
30-
timezoneOffset = 0
31+
timezoneOffset = 0,
32+
metricType: string = 'events-accepted'
3133
): Promise<{ timestamp: number; count: number }[]> {
3234
// Check if Redis is connected
3335
if (!this.redisHelper.isConnected()) {
@@ -37,7 +39,7 @@ export default class ChartDataService {
3739

3840
// Determine granularity and compose key
3941
const granularity = getTimeSeriesSuffix(groupBy);
40-
const key = composeProjectMetricsKey(granularity, projectId);
42+
const key = composeProjectMetricsKey(granularity, projectId, metricType);
4143

4244
// Parse ISO date strings to milliseconds
4345
const start = new Date(startDate).getTime();
@@ -46,6 +48,7 @@ export default class ChartDataService {
4648

4749
// Fetch data from Redis
4850
let result: TsRangeResult[] = [];
51+
4952
try {
5053
result = await this.redisHelper.tsRange(
5154
key,
@@ -65,8 +68,10 @@ export default class ChartDataService {
6568

6669
// Transform data from Redis
6770
const dataPoints: { [ts: number]: number } = {};
71+
6872
for (const [tsStr, valStr] of result) {
6973
const tsMs = Number(tsStr);
74+
7075
dataPoints[tsMs] = Number(valStr) || 0;
7176
}
7277

@@ -79,6 +84,7 @@ export default class ChartDataService {
7984

8085
while (current <= end) {
8186
const count = dataPoints[current] || 0;
87+
8288
filled.push({
8389
timestamp: Math.floor((current + timezoneOffset * 60 * 1000) / 1000),
8490
count,

src/typeDefs/chart.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,19 @@ export default gql`
1212
"""
1313
count: Int
1414
}
15+
16+
"""
17+
Project chart data split by metric type
18+
"""
19+
type ProjectChartSeries {
20+
"""
21+
Accepted events (successfully processed)
22+
"""
23+
accepted: [ChartDataItem!]!
24+
25+
"""
26+
Events rejected due to rate limiting
27+
"""
28+
rateLimited: [ChartDataItem!]
29+
}
1530
`;

src/typeDefs/project.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ type Project {
372372
User's local timezone offset in minutes
373373
"""
374374
timezoneOffset: Int! = 0
375-
): [ChartDataItem]
375+
): ProjectChartSeries
376376
"""
377377
Returns number of unread events
378378
"""

0 commit comments

Comments
 (0)