Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 6 additions & 0 deletions redisinsight/api/src/dto/server.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,10 @@ export class GetServerInfoResponse {
example: ['PLAIN', 'KEYTAR'],
})
encryptionStrategies: string[];

@ApiProperty({
description: 'Server session id.',
type: Number,
})
sessionId: number;
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ describe('ServerOnPremiseService', () => {
let serverRepository: MockType<Repository<ServerEntity>>;
let eventEmitter: EventEmitter2;
let encryptionService: MockType<EncryptionService>;
const sessionId = new Date().getTime();

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
Expand Down Expand Up @@ -88,12 +89,12 @@ describe('ServerOnPremiseService', () => {
serverRepository.findOne.mockResolvedValue(null);
serverRepository.create.mockReturnValue(mockServerEntity);

await service.onApplicationBootstrap();
await service.onApplicationBootstrap(sessionId);

expect(eventEmitter.emit).toHaveBeenNthCalledWith(
1,
AppAnalyticsEvents.Initialize,
mockServerEntity.id,
{ anonymousId: mockServerEntity.id, sessionId },
);
expect(eventEmitter.emit).toHaveBeenNthCalledWith(
2,
Expand All @@ -107,12 +108,12 @@ describe('ServerOnPremiseService', () => {
it('should emit APPLICATION_STARTED on second application launch', async () => {
serverRepository.findOne.mockResolvedValue(mockServerEntity);

await service.onApplicationBootstrap();
await service.onApplicationBootstrap(sessionId);

expect(eventEmitter.emit).toHaveBeenNthCalledWith(
1,
AppAnalyticsEvents.Initialize,
mockServerEntity.id,
{ anonymousId: mockServerEntity.id, sessionId },
);
expect(eventEmitter.emit).toHaveBeenNthCalledWith(
2,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,16 @@ implements OnApplicationBootstrap, IServerProvider {

private encryptionService: EncryptionService;

private sessionId: number;

constructor(repository, eventEmitter, encryptionService) {
this.repository = repository;
this.eventEmitter = eventEmitter;
this.encryptionService = encryptionService;
}

async onApplicationBootstrap() {
async onApplicationBootstrap(sessionId: number = new Date().getTime()) {
this.sessionId = sessionId;
await this.upsertServerInfo();
}

Expand All @@ -45,7 +48,7 @@ implements OnApplicationBootstrap, IServerProvider {
// Create default server info on first application launch
serverInfo = this.repository.create({});
await this.repository.save(serverInfo);
this.eventEmitter.emit(AppAnalyticsEvents.Initialize, serverInfo.id);
this.eventEmitter.emit(AppAnalyticsEvents.Initialize, { anonymousId: serverInfo.id, sessionId: this.sessionId });
this.eventEmitter.emit(AppAnalyticsEvents.Track, {
event: TelemetryEvents.ApplicationFirstStart,
eventData: {
Expand All @@ -57,7 +60,7 @@ implements OnApplicationBootstrap, IServerProvider {
});
} else {
this.logger.log('Application started.');
this.eventEmitter.emit(AppAnalyticsEvents.Initialize, serverInfo.id);
this.eventEmitter.emit(AppAnalyticsEvents.Initialize, { anonymousId: serverInfo.id, sessionId: this.sessionId });
this.eventEmitter.emit(AppAnalyticsEvents.Track, {
event: TelemetryEvents.ApplicationStarted,
eventData: {
Expand All @@ -82,6 +85,7 @@ implements OnApplicationBootstrap, IServerProvider {
}
const result = {
...info,
sessionId: this.sessionId,
appVersion: SERVER_CONFIG.appVersion,
osPlatform: process.platform,
buildType: SERVER_CONFIG.buildType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const mockSettingsWithoutPermission = {
describe('AnalyticsService', () => {
let service: AnalyticsService;
let settingsService: ISettingsProvider;
const sessionId = new Date().getTime();

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
Expand All @@ -58,7 +59,7 @@ describe('AnalyticsService', () => {

describe('initialize', () => {
it('should set anonymousId', () => {
service.initialize(mockAnonymousId);
service.initialize({ anonymousId: mockAnonymousId, sessionId });

const anonymousId = service.getAnonymousId();

Expand All @@ -69,7 +70,7 @@ describe('AnalyticsService', () => {
describe('sendEvent', () => {
beforeEach(() => {
mockAnalyticsTrack = jest.fn();
service.initialize(mockAnonymousId);
service.initialize({ anonymousId: mockAnonymousId, sessionId });
});
it('should send event with anonymousId if permission are granted', async () => {
settingsService.getSettings = jest
Expand All @@ -84,6 +85,7 @@ describe('AnalyticsService', () => {

expect(mockAnalyticsTrack).toHaveBeenCalledWith({
anonymousId: mockAnonymousId,
integrations: { Amplitude: { session_id: sessionId } },
event: TelemetryEvents.ApplicationStarted,
properties: {},
});
Expand Down Expand Up @@ -114,6 +116,7 @@ describe('AnalyticsService', () => {

expect(mockAnalyticsTrack).toHaveBeenCalledWith({
anonymousId: NON_TRACKING_ANONYMOUS_ID,
integrations: { Amplitude: { session_id: sessionId } },
event: TelemetryEvents.ApplicationStarted,
properties: {},
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,17 @@ export interface ITelemetryEvent {
nonTracking: boolean;
}

export interface ITelemetryInitEvent {
anonymousId: string;
sessionId: number;
}

@Injectable()
export class AnalyticsService {
private anonymousId: string = NON_TRACKING_ANONYMOUS_ID;

private sessionId: number = -1;

private analytics;

constructor(
Expand All @@ -31,7 +38,9 @@ export class AnalyticsService {
}

@OnEvent(AppAnalyticsEvents.Initialize)
public initialize(anonymousId: string) {
public initialize(payload: ITelemetryInitEvent) {
const { anonymousId, sessionId } = payload;
this.sessionId = sessionId;
this.anonymousId = anonymousId;
this.analytics = new Analytics(ANALYTICS_CONFIG.writeKey);
}
Expand All @@ -55,6 +64,7 @@ export class AnalyticsService {
if (isAnalyticsGranted) {
this.analytics.track({
anonymousId: this.anonymousId,
integrations: { Amplitude: { session_id: this.sessionId } },
event,
properties: {
...eventData,
Expand All @@ -63,6 +73,7 @@ export class AnalyticsService {
} else if (nonTracking) {
this.analytics.track({
anonymousId: NON_TRACKING_ANONYMOUS_ID,
integrations: { Amplitude: { session_id: this.sessionId } },
event,
properties: {
...eventData,
Expand Down
1 change: 1 addition & 0 deletions redisinsight/api/test/api/info/GET-info.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const responseSchema = Joi.object().keys({
osPlatform: Joi.string().required(),
buildType: Joi.string().valid('ELECTRON', 'DOCKER_ON_PREMISE').required(),
encryptionStrategies: Joi.array().items(Joi.string()),
sessionId: Joi.number().required(),
}).required();

const mainCheckFn = async (testCase) => {
Expand Down
2 changes: 1 addition & 1 deletion redisinsight/ui/src/components/config/Config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const Config = () => {
if (serverInfo && checkIsAnalyticsGranted()) {
(async () => {
const telemetryService = getTelemetryService(segmentWriteKey)
await telemetryService.identify({ installationId: serverInfo.id })
await telemetryService.identify({ installationId: serverInfo.id, sessionId: serverInfo.sessionId })

dispatch(setAnalyticsIdentified(true))
})()
Expand Down
1 change: 1 addition & 0 deletions redisinsight/ui/src/telemetry/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { TelemetryEvent } from './events'

export interface ITelemetryIdentify {
installationId: string;
sessionId: number;
}

export interface ITelemetryService {
Expand Down
23 changes: 19 additions & 4 deletions redisinsight/ui/src/telemetry/segment.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BrowserStorageItem } from 'uiSrc/constants'
import { localStorageService } from 'uiSrc/services'
import { ITelemetryService, ITelemetryEvent } from './interfaces'
import { ITelemetryService, ITelemetryEvent, ITelemetryIdentify } from './interfaces'
import loadSegmentAnalytics from './loadSegmentAnalytics'

export const NON_TRACKING_ANONYMOUS_ID = 'UNSET'
Expand All @@ -19,6 +19,8 @@ interface IContextPageInfo {
export class SegmentTelemetryService implements ITelemetryService {
private _anonymousId: string = ''

private _sessionId: number = -1

private _getPageInfo = (): IContextPage => {
const pageObject: IContextPage = {}

Expand Down Expand Up @@ -52,15 +54,21 @@ export class SegmentTelemetryService implements ITelemetryService {
context: {
ip: '0.0.0.0',
...pageInfo
}
},
integrations: {
Amplitude: {
session_id: this._sessionId,
}
},
}, resolve)
} catch (e) {
reject(e)
}
})
}

async identify({ installationId }: { installationId: string }): Promise<void> {
async identify({ installationId, sessionId }: ITelemetryIdentify): Promise<void> {
this._sessionId = sessionId || -1
if (this._anonymousId) {
return Promise.resolve()
}
Expand Down Expand Up @@ -94,7 +102,14 @@ export class SegmentTelemetryService implements ITelemetryService {
async event({ event, properties }: ITelemetryEvent): Promise<void> {
return new Promise((resolve, reject) => {
try {
window.analytics.track(event, properties, { context: { ip: '0.0.0.0', ...this._getPageInfo() } }, resolve)
window.analytics.track(event, properties, {
context: { ip: '0.0.0.0', ...this._getPageInfo() },
integrations: {
Amplitude: {
session_id: this._sessionId,
}
},
}, resolve)
} catch (e) {
reject(e)
}
Expand Down