Skip to content

Commit 95b316d

Browse files
#RI-2991-add pageView and BE telemetry events (#780)
1 parent 7de6dc0 commit 95b316d

File tree

9 files changed

+198
-9
lines changed

9 files changed

+198
-9
lines changed

redisinsight/api/src/__mocks__/analytics.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,9 @@ export const mockSettingsAnalyticsService = () => ({
2727
sendAnalyticsAgreementChange: jest.fn(),
2828
sendSettingsUpdatedEvent: jest.fn(),
2929
});
30+
31+
export const mockPubSubAnalyticsService = () => ({
32+
sendMessagePublishedEvent: jest.fn(),
33+
sendChannelSubscribeEvent: jest.fn(),
34+
sendChannelUnsubscribeEvent: jest.fn(),
35+
});

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,9 @@ export enum TelemetryEvents {
4545
// Slowlog
4646
SlowlogSetLogSlowerThan = 'SLOWLOG_SET_LOG_SLOWER_THAN',
4747
SlowlogSetMaxLen = 'SLOWLOG_SET_MAX_LEN',
48+
49+
// Pub/Sub
50+
PubSubMessagePublished = 'PUBSUB_MESSAGE_PUBLISHED',
51+
PubSubChannelSubscribed = 'PUBSUB_CHANNEL_SUBSCRIBED',
52+
PubSubChannelUnsubscribed = 'PUBSUB_CHANNEL_UNSUBSCRIBED',
4853
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { Test, TestingModule } from '@nestjs/testing';
2+
import { EventEmitter2 } from '@nestjs/event-emitter';
3+
import { mockStandaloneDatabaseEntity } from 'src/__mocks__';
4+
import { TelemetryEvents } from 'src/constants';
5+
import { PubSubAnalyticsService } from './pub-sub.analytics.service';
6+
7+
const instanceId = mockStandaloneDatabaseEntity.id;
8+
9+
const affected = 2;
10+
11+
describe('PubSubAnalyticsService', () => {
12+
let service: PubSubAnalyticsService;
13+
let sendEventMethod: jest.SpyInstance<PubSubAnalyticsService, unknown[]>;
14+
15+
beforeEach(async () => {
16+
const module: TestingModule = await Test.createTestingModule({
17+
providers: [
18+
EventEmitter2,
19+
PubSubAnalyticsService,
20+
],
21+
}).compile();
22+
23+
service = module.get<PubSubAnalyticsService>(PubSubAnalyticsService);
24+
sendEventMethod = jest.spyOn<PubSubAnalyticsService, any>(
25+
service,
26+
'sendEvent',
27+
);
28+
});
29+
30+
describe('sendMessagePublishedEvent', () => {
31+
it('should emit sendMessagePublished event', () => {
32+
service.sendMessagePublishedEvent(
33+
instanceId,
34+
affected,
35+
);
36+
37+
expect(sendEventMethod).toHaveBeenCalledWith(
38+
TelemetryEvents.PubSubMessagePublished,
39+
{
40+
databaseId: instanceId,
41+
clients: affected,
42+
},
43+
);
44+
});
45+
});
46+
47+
describe('sendChannelSubscribeEvent', () => {
48+
it('should emit sendChannelSubscribe event', () => {
49+
service.sendChannelSubscribeEvent(
50+
instanceId,
51+
);
52+
53+
expect(sendEventMethod).toHaveBeenCalledWith(
54+
TelemetryEvents.PubSubChannelSubscribed,
55+
{
56+
databaseId: instanceId,
57+
},
58+
);
59+
});
60+
});
61+
62+
describe('sendChannelUnsubscribeEvent', () => {
63+
it('should emit sendChannelUnsubscribe event', () => {
64+
service.sendChannelUnsubscribeEvent(
65+
instanceId,
66+
);
67+
68+
expect(sendEventMethod).toHaveBeenCalledWith(
69+
TelemetryEvents.PubSubChannelUnsubscribed,
70+
{
71+
databaseId: instanceId,
72+
},
73+
);
74+
});
75+
});
76+
});
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { Injectable } from '@nestjs/common';
2+
import { EventEmitter2 } from '@nestjs/event-emitter';
3+
import { TelemetryEvents } from 'src/constants';
4+
import { TelemetryBaseService } from 'src/modules/shared/services/base/telemetry.base.service';
5+
import { CommandExecutionStatus } from 'src/modules/cli/dto/cli.dto';
6+
import { RedisError, ReplyError } from 'src/models';
7+
8+
export interface IExecResult {
9+
response: any;
10+
status: CommandExecutionStatus;
11+
error?: RedisError | ReplyError | Error,
12+
}
13+
14+
@Injectable()
15+
export class PubSubAnalyticsService extends TelemetryBaseService {
16+
constructor(protected eventEmitter: EventEmitter2) {
17+
super(eventEmitter);
18+
}
19+
20+
sendMessagePublishedEvent(databaseId: string, affected: number): void {
21+
try {
22+
this.sendEvent(
23+
TelemetryEvents.PubSubMessagePublished,
24+
{
25+
databaseId,
26+
clients: affected,
27+
},
28+
);
29+
} catch (e) {
30+
// continue regardless of error
31+
}
32+
}
33+
34+
sendChannelSubscribeEvent(databaseId: string): void {
35+
try {
36+
this.sendEvent(
37+
TelemetryEvents.PubSubChannelSubscribed,
38+
{
39+
databaseId,
40+
},
41+
);
42+
} catch (e) {
43+
// continue regardless of error
44+
}
45+
}
46+
47+
sendChannelUnsubscribeEvent(databaseId: string): void {
48+
try {
49+
this.sendEvent(
50+
TelemetryEvents.PubSubChannelUnsubscribed,
51+
{
52+
databaseId,
53+
},
54+
);
55+
} catch (e) {
56+
// continue regardless of error
57+
}
58+
}
59+
}

redisinsight/api/src/modules/pub-sub/pub-sub.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ import { UserSessionProvider } from 'src/modules/pub-sub/providers/user-session.
66
import { SubscriptionProvider } from 'src/modules/pub-sub/providers/subscription.provider';
77
import { RedisClientProvider } from 'src/modules/pub-sub/providers/redis-client.provider';
88
import { PubSubController } from 'src/modules/pub-sub/pub-sub.controller';
9+
import { PubSubAnalyticsService } from 'src/modules/pub-sub/pub-sub.analytics.service';
910

1011
@Module({
1112
imports: [SharedModule],
1213
providers: [
1314
PubSubGateway,
1415
PubSubService,
16+
PubSubAnalyticsService,
1517
UserSessionProvider,
1618
SubscriptionProvider,
1719
RedisClientProvider,

redisinsight/api/src/modules/pub-sub/pub-sub.service.spec.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,27 @@
11
import { Test, TestingModule } from '@nestjs/testing';
22
import * as Redis from 'ioredis';
33
import {
4-
mockLogFile, mockRedisShardObserver, mockSocket, mockStandaloneDatabaseEntity,
5-
MockType
4+
// mockLogFile,
5+
// mockRedisShardObserver,
6+
mockSocket,
7+
mockStandaloneDatabaseEntity,
8+
MockType,
9+
mockPubSubAnalyticsService
610
} from 'src/__mocks__';
711
import { InstancesBusinessService } from 'src/modules/shared/services/instances-business/instances-business.service';
8-
import { RedisObserverProvider } from 'src/modules/profiler/providers/redis-observer.provider';
12+
// import { RedisObserverProvider } from 'src/modules/profiler/providers/redis-observer.provider';
913
import { IFindRedisClientInstanceByOptions, RedisService } from 'src/modules/core/services/redis/redis.service';
1014
import { mockRedisClientInstance } from 'src/modules/shared/services/base/redis-consumer.abstract.service.spec';
11-
import { RedisObserverStatus } from 'src/modules/profiler/constants';
15+
// import { RedisObserverStatus } from 'src/modules/profiler/constants';
1216
import { PubSubService } from 'src/modules/pub-sub/pub-sub.service';
1317
import { UserSessionProvider } from 'src/modules/pub-sub/providers/user-session.provider';
1418
import { SubscriptionProvider } from 'src/modules/pub-sub/providers/subscription.provider';
1519
import { UserClient } from 'src/modules/pub-sub/model/user-client';
1620
import { SubscriptionType } from 'src/modules/pub-sub/constants';
17-
import { RedisClientProvider } from 'src/modules/pub-sub/providers/redis-client.provider';
21+
// import { RedisClientProvider } from 'src/modules/pub-sub/providers/redis-client.provider';
1822
import { UserSession } from 'src/modules/pub-sub/model/user-session';
1923
import { RedisClient } from 'src/modules/pub-sub/model/redis-client';
24+
import { PubSubAnalyticsService } from 'src/modules/pub-sub/pub-sub.analytics.service';
2025
import { ForbiddenException, NotFoundException } from '@nestjs/common';
2126

2227
const nodeClient = Object.create(Redis.prototype);
@@ -81,6 +86,10 @@ describe('PubSubService', () => {
8186
removeUserSession: jest.fn(),
8287
}),
8388
},
89+
{
90+
provide: PubSubAnalyticsService,
91+
useFactory: mockPubSubAnalyticsService,
92+
},
8493
{
8594
provide: RedisService,
8695
useFactory: () => ({

redisinsight/api/src/modules/pub-sub/pub-sub.service.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { SubscriptionProvider } from 'src/modules/pub-sub/providers/subscription
66
import { IFindRedisClientInstanceByOptions, RedisService } from 'src/modules/core/services/redis/redis.service';
77
import { PublishResponse } from 'src/modules/pub-sub/dto/publish.response';
88
import { PublishDto } from 'src/modules/pub-sub/dto/publish.dto';
9+
import { PubSubAnalyticsService } from 'src/modules/pub-sub/pub-sub.analytics.service';
910
import { InstancesBusinessService } from 'src/modules/shared/services/instances-business/instances-business.service';
1011
import { catchAclError } from 'src/utils';
1112

@@ -18,6 +19,7 @@ export class PubSubService {
1819
private readonly subscriptionProvider: SubscriptionProvider,
1920
private redisService: RedisService,
2021
private instancesBusinessService: InstancesBusinessService,
22+
private analyticsService: PubSubAnalyticsService,
2123
) {}
2224

2325
/**
@@ -33,6 +35,7 @@ export class PubSubService {
3335
await Promise.all(dto.subscriptions.map((subDto) => session.subscribe(
3436
this.subscriptionProvider.createSubscription(userClient, subDto),
3537
)));
38+
this.analyticsService.sendChannelSubscribeEvent(userClient.getDatabaseId());
3639
} catch (e) {
3740
this.logger.error('Unable to create subscriptions', e);
3841

@@ -57,6 +60,7 @@ export class PubSubService {
5760
await Promise.all(dto.subscriptions.map((subDto) => session.unsubscribe(
5861
this.subscriptionProvider.createSubscription(userClient, subDto),
5962
)));
63+
this.analyticsService.sendChannelUnsubscribeEvent(userClient.getDatabaseId());
6064
} catch (e) {
6165
this.logger.error('Unable to unsubscribe', e);
6266

@@ -81,9 +85,12 @@ export class PubSubService {
8185
this.logger.log('Publishing message.');
8286

8387
const client = await this.getClient(clientOptions);
88+
const affected = await client.publish(dto.channel, dto.message);
89+
90+
this.analyticsService.sendMessagePublishedEvent(clientOptions.instanceId, affected);
8491

8592
return {
86-
affected: await client.publish(dto.channel, dto.message),
93+
affected,
8794
};
8895
} catch (e) {
8996
this.logger.error('Unable to publish a message', e);

redisinsight/ui/src/pages/pubSub/PubSubPage.tsx

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import { EuiTitle } from '@elastic/eui'
2-
import React from 'react'
2+
import React, { useEffect, useState } from 'react'
3+
import { useSelector } from 'react-redux'
4+
import { useParams } from 'react-router-dom'
5+
import { appAnalyticsInfoSelector } from 'uiSrc/slices/app/info'
6+
import { connectedInstanceSelector } from 'uiSrc/slices/instances/instances'
37
import InstanceHeader from 'uiSrc/components/instance-header'
48
import { SubscriptionType } from 'uiSrc/constants/pubSub'
9+
import { sendPageViewTelemetry, TelemetryPageView } from 'uiSrc/telemetry'
510

611
import { MessagesListWrapper, PublishMessage, SubscriptionPanel } from './components'
712

@@ -10,7 +15,26 @@ import styles from './styles.module.scss'
1015
export const PUB_SUB_DEFAULT_CHANNEL = { channel: '*', type: SubscriptionType.PSubscribe }
1116

1217
const PubSubPage = () => {
13-
//
18+
const { identified: analyticsIdentified } = useSelector(appAnalyticsInfoSelector)
19+
const { name: connectedInstanceName } = useSelector(connectedInstanceSelector)
20+
const { instanceId } = useParams<{ instanceId: string }>()
21+
22+
const [isPageViewSent, setIsPageViewSent] = useState(false)
23+
24+
useEffect(() => {
25+
if (connectedInstanceName && !isPageViewSent && analyticsIdentified) {
26+
sendPageView(instanceId)
27+
}
28+
}, [connectedInstanceName, isPageViewSent, analyticsIdentified])
29+
30+
const sendPageView = (instanceId: string) => {
31+
sendPageViewTelemetry({
32+
name: TelemetryPageView.PUBSUB_PAGE,
33+
databaseId: instanceId
34+
})
35+
setIsPageViewSent(true)
36+
}
37+
1438
return (
1539
<>
1640
<InstanceHeader />

redisinsight/ui/src/telemetry/pageViews.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ export enum TelemetryPageView {
44
SETTINGS_PAGE = 'Settings',
55
BROWSER_PAGE = 'Browser',
66
WORKBENCH_PAGE = 'Workbench',
7-
SLOWLOG_PAGE = 'Slow Log'
7+
SLOWLOG_PAGE = 'Slow Log',
8+
PUBSUB_PAGE = 'Pub/Sub'
89
}

0 commit comments

Comments
 (0)