Skip to content

Commit 5ab3e4b

Browse files
#RI-4447 - update bulk actions telemetry (#2105)
* #RI-4447 - update bulk actions telemetry
1 parent ded43a6 commit 5ab3e4b

21 files changed

+316
-30
lines changed

redisinsight/api/src/__mocks__/analytics.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
import { EventEmitter2 } from '@nestjs/event-emitter';
2+
import { BulkActionsAnalyticsService } from 'src/modules/bulk-actions/bulk-actions-analytics.service';
3+
4+
const mockEmitter = new EventEmitter2();
5+
6+
class AnalyticsService extends BulkActionsAnalyticsService {
7+
constructor(protected eventEmitter: EventEmitter2) {
8+
super(eventEmitter);
9+
}
10+
}
11+
112
export const mockInstancesAnalyticsService = () => ({
213
sendInstanceListReceivedEvent: jest.fn(),
314
sendInstanceAddedEvent: jest.fn(),
@@ -29,6 +40,8 @@ export const mockSettingsAnalyticsService = () => ({
2940
sendSettingsUpdatedEvent: jest.fn(),
3041
});
3142

43+
export const mockBulActionsAnalyticsService = new AnalyticsService(mockEmitter);
44+
3245
export const mockPubSubAnalyticsService = () => ({
3346
sendMessagePublishedEvent: jest.fn(),
3447
sendChannelSubscribeEvent: jest.fn(),

redisinsight/api/src/constants/telemetry-events.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ export enum TelemetryEvents {
6464
// Bulk Actions
6565
BulkActionsStarted = 'BULK_ACTIONS_STARTED',
6666
BulkActionsStopped = 'BULK_ACTIONS_STOPPED',
67+
BulkActionsSucceed = 'BULK_ACTIONS_SUCCEED',
68+
BulkActionsFailed = 'BULK_ACTIONS_FAILED',
6769
}
6870

6971
export enum CommandType {

redisinsight/api/src/modules/bulk-actions/bulk-actions-analytics.service.ts

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { Injectable } from '@nestjs/common';
1+
import { HttpException, Injectable } from '@nestjs/common';
22
import { EventEmitter2 } from '@nestjs/event-emitter';
33
import { TelemetryEvents } from 'src/constants';
44
import { TelemetryBaseService } from 'src/modules/analytics/telemetry.base.service';
5+
import { getRangeForNumber, BULK_ACTIONS_BREAKPOINTS } from 'src/utils';
56
import { CommandExecutionStatus } from 'src/modules/cli/dto/cli.dto';
67
import { RedisError, ReplyError } from 'src/models';
78
import { IBulkActionOverview } from 'src/modules/bulk-actions/interfaces/bulk-action-overview.interface';
@@ -20,6 +21,8 @@ export class BulkActionsAnalyticsService extends TelemetryBaseService {
2021
super(eventEmitter);
2122
this.events.set(TelemetryEvents.BulkActionsStarted, this.sendActionStarted.bind(this));
2223
this.events.set(TelemetryEvents.BulkActionsStopped, this.sendActionStopped.bind(this));
24+
this.events.set(TelemetryEvents.BulkActionsSucceed, this.sendActionSucceed.bind(this));
25+
this.events.set(TelemetryEvents.BulkActionsFailed, this.sendActionFailed.bind(this));
2326
}
2427

2528
sendActionStarted(overview: IBulkActionOverview): void {
@@ -36,7 +39,9 @@ export class BulkActionsAnalyticsService extends TelemetryBaseService {
3639
},
3740
progress: {
3841
scanned: overview.progress?.scanned,
42+
scannedRange: getRangeForNumber(overview.progress?.scanned, BULK_ACTIONS_BREAKPOINTS),
3943
total: overview.progress?.total,
44+
totalRange: getRangeForNumber(overview.progress?.total, BULK_ACTIONS_BREAKPOINTS),
4045
},
4146
},
4247
);
@@ -59,12 +64,17 @@ export class BulkActionsAnalyticsService extends TelemetryBaseService {
5964
},
6065
progress: {
6166
scanned: overview.progress?.scanned,
67+
scannedRange: getRangeForNumber(overview.progress?.scanned, BULK_ACTIONS_BREAKPOINTS),
6268
total: overview.progress?.total,
69+
totalRange: getRangeForNumber(overview.progress?.total, BULK_ACTIONS_BREAKPOINTS),
6370
},
6471
summary: {
6572
processed: overview.summary?.processed,
73+
processedRange: getRangeForNumber(overview.summary?.processed, BULK_ACTIONS_BREAKPOINTS),
6674
succeed: overview.summary?.succeed,
75+
succeedRange: getRangeForNumber(overview.summary?.succeed, BULK_ACTIONS_BREAKPOINTS),
6776
failed: overview.summary.failed,
77+
failedRange: getRangeForNumber(overview.summary.failed, BULK_ACTIONS_BREAKPOINTS),
6878
},
6979
},
7080
);
@@ -73,6 +83,54 @@ export class BulkActionsAnalyticsService extends TelemetryBaseService {
7383
}
7484
}
7585

86+
sendActionSucceed(overview: IBulkActionOverview): void {
87+
try {
88+
this.sendEvent(
89+
TelemetryEvents.BulkActionsSucceed,
90+
{
91+
databaseId: overview.databaseId,
92+
type: overview.type,
93+
duration: overview.duration,
94+
filter: {
95+
match: overview.filter?.match === '*' ? '*' : 'PATTERN',
96+
type: overview.filter?.type,
97+
},
98+
progress: {
99+
scanned: overview.progress?.scanned,
100+
scannedRange: getRangeForNumber(overview.progress?.scanned, BULK_ACTIONS_BREAKPOINTS),
101+
total: overview.progress?.total,
102+
totalRange: getRangeForNumber(overview.progress?.total, BULK_ACTIONS_BREAKPOINTS),
103+
},
104+
summary: {
105+
processed: overview.summary?.processed,
106+
processedRange: getRangeForNumber(overview.summary?.processed, BULK_ACTIONS_BREAKPOINTS),
107+
succeed: overview.summary?.succeed,
108+
succeedRange: getRangeForNumber(overview.summary?.succeed, BULK_ACTIONS_BREAKPOINTS),
109+
failed: overview.summary.failed,
110+
failedRange: getRangeForNumber(overview.summary.failed, BULK_ACTIONS_BREAKPOINTS),
111+
},
112+
},
113+
);
114+
} catch (e) {
115+
// continue regardless of error
116+
}
117+
}
118+
119+
sendActionFailed(overview: IBulkActionOverview, error: HttpException | Error): void {
120+
try {
121+
this.sendEvent(
122+
TelemetryEvents.BulkActionsFailed,
123+
{
124+
databaseId: overview.databaseId,
125+
type: overview.type,
126+
error,
127+
},
128+
);
129+
} catch (e) {
130+
// continue regardless of error
131+
}
132+
}
133+
76134
getEventsEmitters(): Map<TelemetryEvents, Function> {
77135
return this.events;
78136
}

redisinsight/api/src/modules/bulk-actions/bulk-actions.service.spec.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as MockedSocket from 'socket.io-mock';
33
import { Test, TestingModule } from '@nestjs/testing';
44
import {
55
MockType,
6+
mockBulActionsAnalyticsService,
67
} from 'src/__mocks__';
78
import { BulkActionsProvider } from 'src/modules/bulk-actions/providers/bulk-actions.provider';
89
import { RedisDataType } from 'src/modules/browser/dto';
@@ -43,6 +44,7 @@ const mockBulkAction = new BulkAction(
4344
mockCreateBulkActionDto.type,
4445
mockBulkActionFilter,
4546
mockSocket1,
47+
mockBulActionsAnalyticsService,
4648
);
4749
const mockOverview = 'mocked overview...';
4850

@@ -72,6 +74,8 @@ describe('BulkActionsService', () => {
7274
useFactory: () => ({
7375
sendActionStarted: jest.fn(),
7476
sendActionStopped: jest.fn(),
77+
sendActionSucceed: jest.fn(),
78+
sendActionFailed: jest.fn(),
7579
}),
7680
},
7781
],

redisinsight/api/src/modules/bulk-actions/bulk-import.service.spec.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { BulkActionsAnalyticsService } from 'src/modules/bulk-actions/bulk-actio
1616
import * as fs from 'fs-extra';
1717
import config from 'src/utils/config';
1818
import { join } from 'path';
19+
import { wrapHttpError } from 'src/common/utils';
1920

2021
const PATH_CONFIG = config.get('dir_path');
2122

@@ -34,6 +35,13 @@ const mockSummary: BulkActionSummary = Object.assign(new BulkActionSummary(), {
3435
errors: [],
3536
});
3637

38+
const mockEmptySummary: BulkActionSummary = Object.assign(new BulkActionSummary(), {
39+
processed: 0,
40+
succeed: 0,
41+
failed: 0,
42+
errors: [],
43+
});
44+
3745
const mockSummaryWithErrors = Object.assign(new BulkActionSummary(), {
3846
processed: 100,
3947
succeed: 99,
@@ -52,6 +60,17 @@ const mockImportResult: IBulkActionOverview = {
5260
duration: 100,
5361
};
5462

63+
const mockEmptyImportResult: IBulkActionOverview = {
64+
id: 'empty',
65+
databaseId: mockClientMetadata.databaseId,
66+
type: BulkActionType.Import,
67+
summary: mockEmptySummary.getOverview(),
68+
progress: null,
69+
filter: null,
70+
status: BulkActionStatus.Completed,
71+
duration: 0,
72+
};
73+
5574
const mockUploadImportFileDto = {
5675
file: {
5776
originalname: 'filename',
@@ -88,6 +107,8 @@ describe('BulkImportService', () => {
88107
useFactory: () => ({
89108
sendActionStarted: jest.fn(),
90109
sendActionStopped: jest.fn(),
110+
sendActionSucceed: jest.fn(),
111+
sendActionFailed: jest.fn(),
91112
}),
92113
},
93114
],
@@ -135,7 +156,7 @@ describe('BulkImportService', () => {
135156
...mockImportResult,
136157
duration: jasmine.anything(),
137158
});
138-
expect(analytics.sendActionStopped).toHaveBeenCalledWith({
159+
expect(analytics.sendActionSucceed).toHaveBeenCalledWith({
139160
...mockImportResult,
140161
duration: jasmine.anything(),
141162
});
@@ -220,6 +241,10 @@ describe('BulkImportService', () => {
220241
fail();
221242
} catch (e) {
222243
expect(mockIORedisClient.disconnect).not.toHaveBeenCalled();
244+
expect(analytics.sendActionFailed).toHaveBeenCalledWith(
245+
{ ...mockEmptyImportResult },
246+
wrapHttpError(e),
247+
);
223248
expect(e).toBeInstanceOf(NotFoundException);
224249
}
225250
});

redisinsight/api/src/modules/bulk-actions/bulk-import.service.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { join, resolve } from 'path';
1+
import { join } from 'path';
22
import * as fs from 'fs-extra';
33
import { BadRequestException, Injectable, Logger } from '@nestjs/common';
44
import { Readable } from 'stream';
@@ -115,6 +115,7 @@ export class BulkImportService {
115115
rl.on('error', (error) => {
116116
result.summary.errors.push(error);
117117
result.status = BulkActionStatus.Failed;
118+
this.analyticsService.sendActionFailed(result, error);
118119
res(null);
119120
});
120121
rl.on('close', () => {
@@ -133,15 +134,19 @@ export class BulkImportService {
133134
result.summary.processed += parseErrors;
134135
result.summary.failed += parseErrors;
135136

136-
this.analyticsService.sendActionStopped(result);
137+
if (result.status === BulkActionStatus.Completed) {
138+
this.analyticsService.sendActionSucceed(result);
139+
}
137140

138141
client.disconnect();
139142

140143
return result;
141144
} catch (e) {
142145
this.logger.error('Unable to process an import file', e);
146+
const exception = wrapHttpError(e);
147+
this.analyticsService.sendActionFailed(result, exception);
143148
client?.disconnect();
144-
throw wrapHttpError(e);
149+
throw exception;
145150
}
146151
}
147152

redisinsight/api/src/modules/bulk-actions/models/bulk-action.spec.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import IORedis from 'ioredis';
22
import { omit } from 'lodash';
33
import {
44
mockSocket,
5+
mockBulActionsAnalyticsService,
56
} from 'src/__mocks__';
67
import {
78
DeleteBulkActionSimpleRunner,
@@ -34,6 +35,23 @@ const mockCreateBulkActionDto = {
3435
type: BulkActionType.Delete,
3536
};
3637

38+
const mockOverview = {
39+
...mockCreateBulkActionDto,
40+
duration: 0,
41+
filter: { match: '*', type: null },
42+
progress: {
43+
scanned: 0,
44+
total: 0,
45+
},
46+
status: 'completed',
47+
summary: {
48+
failed: 0,
49+
processed: 0,
50+
succeed: 0,
51+
errors: [],
52+
},
53+
};
54+
3755
let bulkAction;
3856
let mockRunner;
3957
let mockSummary;
@@ -85,6 +103,7 @@ describe('AbstractBulkActionSimpleRunner', () => {
85103
mockCreateBulkActionDto.type,
86104
mockBulkActionFilter,
87105
mockSocket,
106+
mockBulActionsAnalyticsService,
88107
);
89108
});
90109

@@ -303,8 +322,12 @@ describe('AbstractBulkActionSimpleRunner', () => {
303322
});
304323
describe('sendOverview', () => {
305324
let sendOverviewSpy;
325+
let sendActionSucceedSpy;
326+
let sendActionFailedSpy;
306327

307328
beforeEach(() => {
329+
sendActionSucceedSpy = jest.spyOn(bulkAction['analyticsService'], 'sendActionSucceed');
330+
sendActionFailedSpy = jest.spyOn(bulkAction['analyticsService'], 'sendActionFailed');
308331
sendOverviewSpy = jest.spyOn(bulkAction, 'sendOverview');
309332
});
310333

@@ -320,6 +343,37 @@ describe('AbstractBulkActionSimpleRunner', () => {
320343

321344
expect(sendOverviewSpy).toHaveBeenCalledTimes(1);
322345
});
346+
347+
it('Should call sendActionSucceed', () => {
348+
mockSocket.emit.mockReturnValue();
349+
350+
bulkAction['status'] = BulkActionStatus.Completed;
351+
352+
bulkAction.sendOverview();
353+
354+
expect(sendOverviewSpy).toHaveBeenCalledTimes(1);
355+
expect(sendActionFailedSpy).not.toHaveBeenCalled();
356+
expect(sendActionSucceedSpy).toHaveBeenCalledWith(mockOverview);
357+
});
358+
359+
it('Should call sendActionFailed', () => {
360+
mockSocket.emit.mockReturnValue();
361+
362+
bulkAction['status'] = BulkActionStatus.Failed;
363+
bulkAction['error'] = 'some error';
364+
365+
bulkAction.sendOverview();
366+
367+
expect(sendOverviewSpy).toHaveBeenCalledTimes(1);
368+
expect(sendActionSucceedSpy).not.toHaveBeenCalled();
369+
expect(sendActionFailedSpy).toHaveBeenCalledWith(
370+
{
371+
...mockOverview,
372+
status: 'failed',
373+
},
374+
'some error',
375+
);
376+
});
323377
});
324378
describe('Other', () => {
325379
it('getters', () => {

0 commit comments

Comments
 (0)