diff --git a/redisinsight/api/data/manifest.json b/redisinsight/api/data/manifest.json index 2de02b7d6e..88537278d6 100644 --- a/redisinsight/api/data/manifest.json +++ b/redisinsight/api/data/manifest.json @@ -9,7 +9,7 @@ }, { "path": "search", - "modules": ["search"] + "modules": ["search", "searchlight", "ft", "ftl"] } ] } diff --git a/redisinsight/api/src/__mocks__/bulk-actions.ts b/redisinsight/api/src/__mocks__/bulk-actions.ts index a820eeb0c2..d7ff92c0cc 100644 --- a/redisinsight/api/src/__mocks__/bulk-actions.ts +++ b/redisinsight/api/src/__mocks__/bulk-actions.ts @@ -92,4 +92,5 @@ export const mockBulkActionsAnalytics = () => ({ sendActionStopped: jest.fn(), sendActionSucceed: jest.fn(), sendActionFailed: jest.fn(), + sendImportSamplesUploaded: jest.fn(), }); diff --git a/redisinsight/api/src/constants/telemetry-events.ts b/redisinsight/api/src/constants/telemetry-events.ts index 95b167aa78..1eb1b9dce2 100644 --- a/redisinsight/api/src/constants/telemetry-events.ts +++ b/redisinsight/api/src/constants/telemetry-events.ts @@ -78,6 +78,7 @@ export enum TelemetryEvents { BulkActionsStopped = 'BULK_ACTIONS_STOPPED', BulkActionsSucceed = 'BULK_ACTIONS_SUCCEED', BulkActionsFailed = 'BULK_ACTIONS_FAILED', + ImportSamplesUploaded = 'IMPORT_SAMPLES_UPLOADED', // Feature FeatureFlagConfigUpdated = 'FEATURE_FLAG_CONFIG_UPDATED', diff --git a/redisinsight/api/src/modules/analytics/telemetry.base.service.ts b/redisinsight/api/src/modules/analytics/telemetry.base.service.ts index 8512b2524e..7071c09060 100644 --- a/redisinsight/api/src/modules/analytics/telemetry.base.service.ts +++ b/redisinsight/api/src/modules/analytics/telemetry.base.service.ts @@ -1,8 +1,9 @@ import { isString } from 'lodash'; import { EventEmitter2 } from '@nestjs/event-emitter'; -import { HttpException } from '@nestjs/common'; +import { HttpException, Injectable } from '@nestjs/common'; import { AppAnalyticsEvents } from 'src/constants'; +@Injectable() export abstract class TelemetryBaseService { constructor( protected readonly eventEmitter: EventEmitter2, diff --git a/redisinsight/api/src/modules/bulk-actions/bulk-actions.analytics.spec.ts b/redisinsight/api/src/modules/bulk-actions/bulk-actions.analytics.spec.ts index 12bf94e45a..3c2aab7d94 100644 --- a/redisinsight/api/src/modules/bulk-actions/bulk-actions.analytics.spec.ts +++ b/redisinsight/api/src/modules/bulk-actions/bulk-actions.analytics.spec.ts @@ -223,4 +223,48 @@ describe('BulkActionsAnalytics', () => { expect(sendFailedEventSpy).not.toHaveBeenCalled(); }); }); + + describe('sendImportSamplesUploaded', () => { + it('should emit event when action succeed (without progress)', () => { + service.sendImportSamplesUploaded(mockBulkActionOverview); + + expect(sendEventSpy).toHaveBeenCalledWith( + TelemetryEvents.ImportSamplesUploaded, + { + databaseId: mockBulkActionOverview.databaseId, + action: mockBulkActionOverview.type, + duration: mockBulkActionOverview.duration, + summary: { + processed: mockBulkActionOverview.summary.processed, + processedRange: '0 - 5 000', + succeed: mockBulkActionOverview.summary.succeed, + succeedRange: '0 - 5 000', + failed: mockBulkActionOverview.summary.failed, + failedRange: '0 - 5 000', + }, + }, + ); + }); + it('should emit event when action succeed without filter and summary', () => { + service.sendImportSamplesUploaded({ + ...mockBulkActionOverview, + filter: undefined, + summary: undefined, + }); + + expect(sendEventSpy).toHaveBeenCalledWith( + TelemetryEvents.ImportSamplesUploaded, + { + databaseId: mockBulkActionOverview.databaseId, + action: mockBulkActionOverview.type, + duration: mockBulkActionOverview.duration, + summary: {}, + }, + ); + }); + it('should not emit event in case of an error and should not fail', () => { + service.sendImportSamplesUploaded(undefined); + expect(sendEventSpy).not.toHaveBeenCalled(); + }); + }); }); diff --git a/redisinsight/api/src/modules/bulk-actions/bulk-actions.analytics.ts b/redisinsight/api/src/modules/bulk-actions/bulk-actions.analytics.ts index 77003f1f07..8c5f7e3b15 100644 --- a/redisinsight/api/src/modules/bulk-actions/bulk-actions.analytics.ts +++ b/redisinsight/api/src/modules/bulk-actions/bulk-actions.analytics.ts @@ -105,4 +105,27 @@ export class BulkActionsAnalytics extends TelemetryBaseService { // continue regardless of error } } + + sendImportSamplesUploaded(overview: IBulkActionOverview): void { + try { + this.sendEvent( + TelemetryEvents.ImportSamplesUploaded, + { + databaseId: overview.databaseId, + action: overview.type, + duration: overview.duration, + summary: { + processed: overview.summary?.processed, + processedRange: getRangeForNumber(overview.summary?.processed, BULK_ACTIONS_BREAKPOINTS), + succeed: overview.summary?.succeed, + succeedRange: getRangeForNumber(overview.summary?.succeed, BULK_ACTIONS_BREAKPOINTS), + failed: overview.summary?.failed, + failedRange: getRangeForNumber(overview.summary?.failed, BULK_ACTIONS_BREAKPOINTS), + }, + }, + ); + } catch (e) { + // continue regardless of error + } + } } diff --git a/redisinsight/api/src/modules/bulk-actions/bulk-import.service.spec.ts b/redisinsight/api/src/modules/bulk-actions/bulk-import.service.spec.ts index 46daee947e..206f59a2f9 100644 --- a/redisinsight/api/src/modules/bulk-actions/bulk-import.service.spec.ts +++ b/redisinsight/api/src/modules/bulk-actions/bulk-import.service.spec.ts @@ -346,6 +346,60 @@ describe('BulkImportService', () => { expect(spy).toHaveBeenCalledWith(mockClientMetadata, mockCombinedStream); }); + it('should import default data for search module', async () => { + mockedFs.readFileSync.mockImplementationOnce(() => Buffer.from(JSON.stringify({ + files: [ + { + path: 'some-path', + modules: ['search', 'searchlight', 'ft', 'ftl'], + }, + ], + }))); + + mockedFs.createReadStream.mockImplementationOnce(() => new fs.ReadStream()); + deviceService.get.mockResolvedValue({ + ...mockDatabase, + modules: [{ + name: 'search', + version: 999999, + semanticVersion: '99.99.99', + }], + }); + + await service.importDefaultData(mockClientMetadata); + + expect(mockCombinedStream.append).toHaveBeenCalledTimes(2); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith(mockClientMetadata, mockCombinedStream); + }); + + it('should import default data for searchlight module', async () => { + mockedFs.readFileSync.mockImplementationOnce(() => Buffer.from(JSON.stringify({ + files: [ + { + path: 'some-path', + modules: ['search', 'searchlight', 'ft', 'ftl'], + }, + ], + }))); + + mockedFs.createReadStream.mockImplementationOnce(() => new fs.ReadStream()); + deviceService.get.mockResolvedValue({ + ...mockDatabase, + modules: [{ + name: 'searchlight', + version: 999999, + semanticVersion: '99.99.99', + }], + }); + + await service.importDefaultData(mockClientMetadata); + + expect(mockCombinedStream.append).toHaveBeenCalledTimes(2); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith(mockClientMetadata, mockCombinedStream); + }); + it('should import default data for core module only', async () => { mockedFs.readFileSync.mockImplementationOnce(() => Buffer.from(JSON.stringify(mockDefaultDataManifest))); mockedFs.createReadStream.mockImplementationOnce(() => new fs.ReadStream()); diff --git a/redisinsight/api/src/modules/bulk-actions/bulk-import.service.ts b/redisinsight/api/src/modules/bulk-actions/bulk-import.service.ts index b82a43018e..4d91c839cc 100644 --- a/redisinsight/api/src/modules/bulk-actions/bulk-import.service.ts +++ b/redisinsight/api/src/modules/bulk-actions/bulk-import.service.ts @@ -212,7 +212,11 @@ export class BulkImportService { commandsStream.append('\r\n'); }); - return this.import(clientMetadata, commandsStream); + const result = await this.import(clientMetadata, commandsStream); + + this.analytics.sendImportSamplesUploaded(result); + + return result; } catch (e) { this.logger.error('Unable to process an import file path from tutorial', e); throw new InternalServerErrorException(ERROR_MESSAGES.COMMON_DEFAULT_IMPORT_ERROR);