Skip to content

RI-6855: Add vector search flow #4818

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 159 commits into from
Aug 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
159 commits
Select commit Hold shift + click to select a range
d377072
add redis-ui
pd-redis May 7, 2025
3a3696b
add forms/button
pd-redis May 7, 2025
ba385ab
run format
pd-redis May 7, 2025
049a991
add icons
pd-redis May 7, 2025
3c3b65f
add general export
pd-redis May 7, 2025
1832c1a
re-export icons from ui-icons
pd-redis May 7, 2025
e5ed77b
add theme config in themeContext.tsx
pd-redis May 7, 2025
8026a6c
make SecondaryButton.tsx outlined by default
pd-redis May 7, 2025
36e23a1
add EmptyButton.tsx
pd-redis May 7, 2025
e2a9bfe
add key panels
pd-redis May 7, 2025
f580b98
icon button added
pd-redis May 7, 2025
192aa1f
edit json icons
pd-redis May 7, 2025
e9358e0
analysis page
pd-redis May 8, 2025
f1051e2
update font size
pd-redis May 8, 2025
31da78e
DatetimeForm.tsx
pd-redis May 8, 2025
40cbb55
CloudSettings.tsx
pd-redis May 8, 2025
75db002
add play and play filled icons
pd-redis May 8, 2025
52771a4
QueryCardHeader.tsx
pd-redis May 8, 2025
d07d5d2
QueryActions.tsx
pd-redis May 8, 2025
eee47b2
InternalPage.tsx
pd-redis May 8, 2025
aaaa43b
Merge branch 'main' into fe/feature/RI-7039-replace-eui
pd-redis May 12, 2025
6301a55
move all icon imports
pd-redis May 12, 2025
05ef986
checkpoint
pd-redis May 12, 2025
88bfbe9
add db dialog
pd-redis May 12, 2025
4966145
notifications
pd-redis May 12, 2025
226baf2
remove unused imports
pd-redis May 12, 2025
1acb93a
fix ZSetDetails.tsx overflow
pd-redis May 12, 2025
e836924
command helper
pd-redis May 12, 2025
f744afc
refactor window controls
pd-redis May 12, 2025
0f0e74f
checkpoint
pd-redis May 12, 2025
a02015d
stream
pd-redis May 12, 2025
5c04afc
monitor, browser search, connection
pd-redis May 13, 2025
321112b
InstanceHeader.tsx, WbNoResultsMessage.tsx
pd-redis May 13, 2025
735ad96
rdi
pd-redis May 13, 2025
42a16fb
InlineItemEditor.tsx
pd-redis May 13, 2025
6803376
rdi
pd-redis May 13, 2025
4e1f2d3
Merge branch 'main' into fe/feature/RI-7039-replace-eui
pd-redis May 14, 2025
c4045c9
NotFoundErrorPage.tsx
pd-redis May 14, 2025
4937213
update to public packages
pd-redis May 14, 2025
f33e3da
ensure color is supported
pd-redis May 14, 2025
d4ef6ef
connectivity screens
pd-redis May 14, 2025
85ab56d
ConnectivityOptions.tsx
pd-redis May 14, 2025
47ab453
Recommendations.tsx
pd-redis May 14, 2025
0d1e8ee
update vite config
KrumTy May 15, 2025
38a54bb
update jest config
KrumTy May 15, 2025
88c342b
OnboardingStartPopover.tsx
pd-redis May 14, 2025
5775639
CodeButtonBlock.tsx
pd-redis May 15, 2025
b58bb78
RunConfirmationPopover.tsx
pd-redis May 15, 2025
b916a2a
UploadTutorialForm.tsx
pd-redis May 15, 2025
03855eb
RedisUploadButton.tsx
pd-redis May 15, 2025
eaa652e
BulkUpload.tsx
pd-redis May 15, 2025
e78731f
FilterNotAvailable.tsx
pd-redis May 15, 2025
e098586
ModuleNotLoadedButton.tsx
pd-redis May 15, 2025
12f628a
ModuleNotLoadedMinimalized.tsx
pd-redis May 15, 2025
f976c0c
MonacoEditor.tsx
pd-redis May 15, 2025
8c831cb
CloudCapiUnAuthorizedErrorContent.tsx
pd-redis May 15, 2025
822c0e9
InfiniteMessages.tsx
pd-redis May 15, 2025
d24f422
OAuthConnectFreeDb.tsx, RdiDeployErrorContent.tsx, Link.tsx
pd-redis May 15, 2025
78f808d
OAuthSelectAccountDialog.tsx
pd-redis May 15, 2025
51a6f74
OAuthSelectPlan.tsx
pd-redis May 15, 2025
c311f89
OAuthSignInButton.tsx
pd-redis May 15, 2025
12a402b
OAuthAutodiscovery.tsx
pd-redis May 15, 2025
02aa2aa
OAuthCreateDb.tsx
pd-redis May 15, 2025
4893c44
OAuthSsoForm.tsx
pd-redis May 15, 2025
355db6c
InternalLink.tsx, OnboardingTour.tsx
pd-redis May 15, 2025
96a592a
VoteOption.tsx
pd-redis May 15, 2025
f043f43
ScanMore.tsx
pd-redis May 15, 2025
4817dfc
ChatForm.tsx, ErrorMessage.tsx, ExpertChatHeader.tsx
pd-redis May 15, 2025
1d6d38a
RestartChat.tsx
pd-redis May 15, 2025
7ef621e
DeleteTutorialButton.tsx
pd-redis May 15, 2025
3b897fa
PopoverRunAnalyze.tsx
pd-redis May 15, 2025
ba26fac
CopilotTrigger.tsx
pd-redis May 15, 2025
6db7083
RedisCloudDatabasesResult.tsx
pd-redis May 15, 2025
960bcc9
RedisCloudDatabases.tsx
pd-redis May 16, 2025
ba662a9
RedisCloudSubscriptions.tsx
pd-redis May 16, 2025
ee882ea
SentinelDatabasesResult.tsx, SentinelDatabasesResultPage.tsx
pd-redis May 16, 2025
79e5e29
SentinelDatabases.tsx
pd-redis May 16, 2025
ecefce7
BulkDeleteFooter.tsx
pd-redis May 16, 2025
93c5de6
BulkDeleteSummaryButton.tsx
pd-redis May 16, 2025
c9aeaf9
CreateRedisearchIndex.tsx
pd-redis May 16, 2025
c2df435
RI-7051: Replace EuiFieldPassword with PasswordInput (#4552)
KrumTy May 20, 2025
97b2d46
fix Config.spec.tsx
pd-redis May 28, 2025
901d5ec
fic AddKeyList.spec.tsx, AddKeyList.tsx, RdiDeployErrorContent.tsx, R…
pd-redis May 28, 2025
7de2983
fic SentinelDatabasesResultPage.tsx
pd-redis May 28, 2025
6553188
RI-7053: replace EuiFlyout with Drawer (#4582)
KrumTy May 28, 2025
ffda0f7
RI-7054: replace EuiFormRow with FormField (#4585)
KrumTy May 28, 2025
839830c
RI-7052: replace EuiFieldSearch with SearchInput (#4586)
KrumTy May 28, 2025
66a4de2
RI-7056 replace eui health (#4593)
pd-redis May 29, 2025
4f09c94
RI-7045: replace EuiCallOut
pd-redis May 29, 2025
b3ba28e
RI-7044 , RI-7043: EuiButtonEmpty, EuiButtonIcon
pd-redis May 29, 2025
a4ca899
RI-7046: replace EuiCheckbox
pd-redis Jun 4, 2025
5f12ecf
RI-7047: replace eui combo box
pd-redis Jun 4, 2025
0c385bd
RI-7041: replace eui badge
pd-redis Jun 4, 2025
e98b604
RI-7055: replace eui global toast
pd-redis Jun 5, 2025
5fc5800
RI-7070: RI-7072 replace eui text, eui colortext
pd-redis Jun 5, 2025
84a3d21
RI-7050 replace EUI field number with NumericInput (#4607)
dantovska Jun 6, 2025
0b87f59
RI-7048, RI-7049: replace eui menu with redis menu (#4611)
dantovska Jun 9, 2025
4cb33d0
RI-7071: Replace EuiTextArea with TextArea (#4619)
KrumTy Jun 9, 2025
1469bda
replace euitext with colortext
pd-redis Jun 10, 2025
662ee58
RI-7073 replace eui title
pd-redis Jun 10, 2025
bf3ddfe
RI-7068: replace EuiSwitch with SwitchInput (#4622)
KrumTy Jun 16, 2025
384bdff
[RI-7069]: Replace EuiTabs with Tabs (#4625)
KrumTy Jun 16, 2025
62f8a72
RI-7059: Replace EUI Link with Redis Link (#4620)
dantovska Jun 24, 2025
01d6ef4
RI-7060: Replace EUI Loading Spinner with Redis Loader (#4631)
dantovska Jun 24, 2025
debba5d
[RI-7058] Replace EuiInMemoryTable with Table (#4640)
KrumTy Jun 30, 2025
face974
RI-7179: replace eui tour step
pd-redis Jun 30, 2025
1320c70
RI-7063: replace EuiPanel with Card (#4655)
KrumTy Jul 1, 2025
e075507
RI-7066: replace eui radio group and eui SuperSelect, RI-7067 (#4645)
pd-redis Jul 2, 2025
7f0bbd4
remove cx
pd-redis Jul 2, 2025
bd05295
[RI-7074] Replace EuiToolTip with RiTooltip (#4659)
KrumTy Jul 4, 2025
c7bb583
Merge branch 'main' into fe/feature/RI-7039-replace-eui
pd-redis Jul 8, 2025
59650e0
fix errors after main merge
pd-redis Jul 9, 2025
6d9f45f
RI-7040: replace eui accordion
pd-redis Jul 9, 2025
f93a9a7
[RI-7064] Replace EuiPopover with RiPopover (#4671)
KrumTy Jul 10, 2025
e3f0b73
[RI-7074] revert anchorClassName prop usage for RiTooltip (#4710)
KrumTy Jul 10, 2025
0dd6e01
RI-7065: Replace EUI Progress with custom Progress Bar Loader (#4663)
dantovska Jul 13, 2025
483390b
RI-7062: Replace EUI sidebar with Redis SideBar (#4660)
dantovska Jul 14, 2025
4c6983b
Fe/feature/ri 7039 replace eui build fix 2 (#4721)
KIvanow Jul 15, 2025
a7f885d
RI-7040: replace eui icon
pd-redis Jul 17, 2025
b993a06
[RI-7040] RiIcon refactor (#4727)
KrumTy Jul 18, 2025
0664a81
RI-7191 Selector tabs component (#4670)
valkirilov Jul 11, 2025
7dbe462
RI-7189: Add SelectionBox component (#4702)
pawelangelow Jul 18, 2025
556c73d
[RI-7194] Add "Create vector search" wizard (#4718)
KrumTy Jul 21, 2025
d69f0c5
RI-7190 Field Box component (#4716)
valkirilov Jul 23, 2025
8992b63
RI-7192: add generateFtCreateCommand() util (#4715)
pawelangelow Jul 23, 2025
edbd45b
RI-7197 Add API endpoint for deleting Redis indexes (#4748)
valkirilov Jul 24, 2025
a0c099a
RI-7197 Introduce vector search actions panel (#4741)
valkirilov Jul 25, 2025
4cb651a
RI-7237 Rework tabs component for vector search flow
valkirilov Jul 29, 2025
e169eca
RI-7193: Introduce useCreateIndex() (#4767)
pawelangelow Jul 29, 2025
c9fbb00
RI-7197 Introduce vector search "Manage Indexes" drawer (#4751)
valkirilov Jul 30, 2025
e83b984
merge latest changes from "fe/feature/RI-7039-replace-eui" (#4782)
KrumTy Jul 31, 2025
bfce199
[RI-7194] Create vector search index wizard (#4781)
KrumTy Jul 31, 2025
793b983
Introduce factories for mocking test data (#4772)
valkirilov Aug 1, 2025
a2e14f3
RI-7197 Rework the manage index section (#4785)
valkirilov Aug 4, 2025
71bcb66
RI-7195: Extend Query component (#4794)
pawelangelow Aug 4, 2025
bb18d5d
RI-7195: Add search screen (#4788)
pawelangelow Aug 5, 2025
30560d7
RI-7218 Add telemetry collection for Vector Search (#4799)
valkirilov Aug 5, 2025
5a6a10f
RI-6855: Sync latest base branch changes (#4808)
pawelangelow Aug 6, 2025
c61328c
RI-7196: add SavedQueriesScreen (#4803)
KrumTy Aug 6, 2025
8ff116a
[RI-7194] fix Create Index style issues (#4813)
KrumTy Aug 7, 2025
fba5798
[RI-6855] merge main into vector-search (#4816)
KrumTy Aug 7, 2025
f105408
RI-7287: add missing Create Index icons (#4822)
KrumTy Aug 7, 2025
4580012
RI-7289: adjust Create Index wizard spacing to match style of Workben…
KrumTy Aug 7, 2025
ea23cfb
Merge remote-tracking branch 'origin/main' into feature/RI-6855/vecto…
KrumTy Aug 7, 2025
3310c53
fix tests workflow
KrumTy Aug 7, 2025
b4b839f
RI-7218 Add telemetry collection for Create Vector Search Index wizar…
valkirilov Aug 7, 2025
d7c6562
RI-7302: Add navigation link (#4823)
pawelangelow Aug 8, 2025
92819f5
RI-7299: Fix "Command preview" spacing (#4825)
pawelangelow Aug 8, 2025
2d251e0
RI-7218 Add telemetry collection for Saved Queries panel on Vector Se…
valkirilov Aug 8, 2025
0cfce38
RI-7218 Add telemetry collection for Manage Indexes drawer on Vector …
valkirilov Aug 8, 2025
5aa7be6
RI-7218 Add telemetry collection for Vector Search page (#4811)
valkirilov Aug 8, 2025
896bfb5
RI-7298: Rework delete confirmation (#4831)
pawelangelow Aug 11, 2025
5df430c
RI-6855: Fix flaky tests (#4836)
pawelangelow Aug 12, 2025
8b5fedb
RI-7293: update vector search routes (#4847)
KrumTy Aug 12, 2025
9bd6273
RI-7218: add empty queries list text
KrumTy Aug 12, 2025
68f3258
RI-7297: update vector search header action buttons style (#4849)
KrumTy Aug 12, 2025
1c4ac32
RI-7317: Add Wizard flow start button (#4845)
pawelangelow Aug 12, 2025
cf34065
RI-7294: Fix borders style (#4834)
pawelangelow Aug 13, 2025
341fb9d
RI-7303: Store create index command in Search results (#4835)
pawelangelow Aug 13, 2025
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
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ module.exports = {
'sonarjs/no-identical-functions': 'error',
'sonarjs/prefer-immediate-return': 'error',
'sonarjs/no-small-switch': 'error',
'sonarjs/no-nested-template-literals': 'off',
'no-console': 'error',
'import/no-duplicates': 'error',
'prefer-destructuring': 'error',
Expand Down
1 change: 1 addition & 0 deletions jest.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ module.exports = {
'\\.scss\\?inline$': '<rootDir>/redisinsight/__mocks__/scssRaw.js',
'uiSrc/slices/store$': '<rootDir>/redisinsight/ui/src/utils/test-store.ts',
'uiSrc/(.*)': '<rootDir>/redisinsight/ui/src/$1',
'apiSrc/(.*)': '<rootDir>/redisinsight/api/src/$1',
'@redislabsdev/redis-ui-components': '@redis-ui/components',
'@redislabsdev/redis-ui-styles': '@redis-ui/styles',
'@redislabsdev/redis-ui-icons': '@redis-ui/icons',
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
"@babel/preset-react": "^7.22.15",
"@babel/preset-typescript": "^7.23.2",
"@electron/rebuild": "^3.7.1",
"@faker-js/faker": "^9.9.0",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
"@svgr/webpack": "^8.1.0",
"@teamsupercell/typings-for-css-modules-loader": "^2.4.0",
Expand Down Expand Up @@ -180,6 +181,7 @@
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-sonarjs": "^2.0.4",
"file-loader": "^6.0.0",
"fishery": "^2.3.1",
"google-auth-library": "^9.0.0",
"googleapis": "^125.0.0",
"html-webpack-plugin": "^5.6.0",
Expand Down
111 changes: 111 additions & 0 deletions redisinsight/api/data/vector-collections/bikes

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsDefined } from 'class-validator';
import { RedisString } from 'src/common/constants';
import { IsRedisString, RedisStringType } from 'src/common/decorators';

export class IndexDeleteRequestBodyDto {
@ApiProperty({
description: 'Index name',
type: String,
})
@IsDefined()
@RedisStringType()
@IsRedisString()
index: RedisString;
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ export class IndexDefinitionDto {
})
@Expose()
default_score: string;

@ApiProperty({
description:
'Indicates whether all fields of a JSON document are automatically indexed by RediSearch',
type: String,
})
@Expose()
indexes_all?: string;
}

export class IndexAttibuteDto {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './create.redisearch-index.dto';
export * from './search.redisearch.dto';
export * from './list.redisearch-indexes.response';
export * from './index.info.dto';
export * from './index.delete.dto';
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
Body,
Controller,
Delete,
Get,
HttpCode,
Post,
Expand All @@ -24,6 +25,7 @@ import { RedisearchService } from 'src/modules/browser/redisearch/redisearch.ser
import { ClientMetadata } from 'src/common/models';
import { BrowserSerializeInterceptor } from 'src/common/interceptors';
import { BrowserBaseController } from 'src/modules/browser/browser.base.controller';
import { IndexDeleteRequestBodyDto } from './dto/index.delete.dto';

@ApiTags('Browser: RediSearch')
@UseInterceptors(BrowserSerializeInterceptor)
Expand Down Expand Up @@ -80,4 +82,14 @@ export class RedisearchController extends BrowserBaseController {
): Promise<IndexInfoDto> {
return await this.service.getInfo(clientMetadata, dto);
}

@Delete('')
@HttpCode(204)
@ApiOperation({ description: 'Delete index' })
async delete(
@BrowserClientMetadata() clientMetadata: ClientMetadata,
@Body() dto: IndexDeleteRequestBodyDto,
): Promise<void> {
return await this.service.deleteIndex(clientMetadata, dto);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
ConflictException,
ForbiddenException,
InternalServerErrorException,
NotFoundException,
} from '@nestjs/common';
import { when } from 'jest-when';
import {
Expand Down Expand Up @@ -453,4 +454,71 @@ describe('RedisearchService', () => {
).rejects.toThrow(InternalServerErrorException);
});
});

describe('deleteIndex', () => {
it('should delete index for standalone', async () => {
const mockIndexName = 'idx:movie';
when(standaloneClient.sendCommand)
.calledWith(expect.arrayContaining(['FT.DROPINDEX']))
.mockResolvedValue(undefined);

await service.deleteIndex(mockBrowserClientMetadata, {
index: mockIndexName,
});

expect(standaloneClient.sendCommand).toHaveBeenCalledWith(
['FT.DROPINDEX', mockIndexName],
{ replyEncoding: 'utf8' },
);
});

it('should delete index for cluster', async () => {
const mockIndexName = 'idx:movie';
databaseClientFactory.getOrCreateClient = jest
.fn()
.mockResolvedValue(clusterClient);
when(clusterClient.sendCommand)
.calledWith(expect.arrayContaining(['FT.DROPINDEX']))
.mockResolvedValue(undefined);

await service.deleteIndex(mockBrowserClientMetadata, {
index: mockIndexName,
});

expect(clusterClient.sendCommand).toHaveBeenCalledWith(
['FT.DROPINDEX', mockIndexName],
{ replyEncoding: 'utf8' },
);
});

it('should handle index not found error', async () => {
const mockIndexName = 'idx:movie';
when(standaloneClient.sendCommand)
.calledWith(expect.arrayContaining(['FT.DROPINDEX']))
.mockRejectedValue(mockRedisUnknownIndexName);

try {
await service.deleteIndex(mockBrowserClientMetadata, {
index: mockIndexName,
});
} catch (e) {
expect(e).toBeInstanceOf(NotFoundException);
}
});

it('should handle ACL error', async () => {
const mockIndexName = 'idx:movie';
when(standaloneClient.sendCommand)
.calledWith(expect.arrayContaining(['FT.DROPINDEX']))
.mockRejectedValue(mockRedisNoPermError);

try {
await service.deleteIndex(mockBrowserClientMetadata, {
index: mockIndexName,
});
} catch (e) {
expect(e).toBeInstanceOf(ForbiddenException);
}
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
ConflictException,
Injectable,
Logger,
NotFoundException,
} from '@nestjs/common';
import ERROR_MESSAGES from 'src/constants/error-messages';
import { catchRedisSearchError } from 'src/utils';
Expand All @@ -28,6 +29,7 @@ import {
RedisClientNodeRole,
} from 'src/modules/redis/client';
import { convertIndexInfoReply } from '../utils/redisIndexInfo';
import { IndexDeleteRequestBodyDto } from './dto/index.delete.dto';

@Injectable()
export class RedisearchService {
Expand Down Expand Up @@ -269,6 +271,36 @@ export class RedisearchService {
}
}

public async deleteIndex(
clientMetadata: ClientMetadata,
dto: IndexDeleteRequestBodyDto,
): Promise<void> {
this.logger.debug('Deleting redisearch index ', clientMetadata);

try {
const { index } = dto;
const client: RedisClient =
await this.databaseClientFactory.getOrCreateClient(clientMetadata);

await client.sendCommand(['FT.DROPINDEX', index], {
replyEncoding: 'utf8',
});

this.logger.debug(
'Successfully deleted redisearch index ',
clientMetadata,
);
} catch (error) {
this.logger.error(
'Failed to delete redisearch index ',
error,
clientMetadata,
);

throw catchRedisSearchError(error);
}
}

/**
* Get array of shards (client per each master node)
* for STANDALONE will return array with a single shard
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ import { BulkImportService } from 'src/modules/bulk-actions/bulk-import.service'
import { ClientMetadataParam } from 'src/common/decorators';
import { ClientMetadata } from 'src/common/models';
import { IBulkActionOverview } from 'src/modules/bulk-actions/interfaces/bulk-action-overview.interface';
import { UploadImportFileByPathDto } from 'src/modules/bulk-actions/dto/upload-import-file-by-path.dto';
import {
UploadImportFileByPathDto,
ImportVectorCollectionDto,
} from 'src/modules/bulk-actions/dto/upload-import-file-by-path.dto';

@UsePipes(new ValidationPipe({ transform: true }))
@UseInterceptors(ClassSerializerInterceptor)
Expand Down Expand Up @@ -85,4 +88,21 @@ export class BulkImportController {
): Promise<IBulkActionOverview> {
return this.service.importDefaultData(clientMetadata);
}

@Post('/vector-collection')
@HttpCode(200)
@ApiEndpoint({
description: 'Import vector collection data',
responses: [
{
type: Object,
},
],
})
async importVectorCollection(
@Body() dto: ImportVectorCollectionDto,
@ClientMetadataParam() clientMetadata: ClientMetadata,
): Promise<IBulkActionOverview> {
return this.service.importVectorCollection(clientMetadata, dto);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Test, TestingModule } from '@nestjs/testing';
import { BulkImportService } from 'src/modules/bulk-actions/bulk-import.service';
import {
mockBulkActionsAnalytics,
mockBulkActionOverviewMatcher,
mockClientMetadata,
mockClusterRedisClient,
mockCombinedStream,
Expand Down Expand Up @@ -541,4 +542,81 @@ describe('BulkImportService', () => {
}
});
});

describe('importVectorCollection', () => {
afterEach(() => {
jest.clearAllMocks();
});

beforeEach(() => {
(mockedFs.pathExists as jest.Mock).mockReset();
(mockedFs.createReadStream as jest.Mock).mockReset();
});

it('should import vector collection successfully', async () => {
const spy = jest.spyOn(service, 'import');
spy.mockResolvedValue(mockBulkActionOverviewMatcher);

(mockedFs.pathExists as jest.Mock).mockResolvedValue(true);
(mockedFs.createReadStream as jest.Mock).mockReturnValue(new Readable());

const result = await service.importVectorCollection(mockClientMetadata, {
collectionName: 'bikes',
});

expect(mockedFs.pathExists).toHaveBeenCalledWith(
expect.stringContaining('vector-collections/bikes'),
);
expect(mockedFs.createReadStream).toHaveBeenCalledWith(
expect.stringContaining('vector-collections/bikes'),
);
expect(spy).toHaveBeenCalledWith(
mockClientMetadata,
expect.any(Readable),
);
expect(result).toEqual(mockBulkActionOverviewMatcher);
});

it('should throw BadRequestException when collectionName file does not exist', async () => {
(mockedFs.pathExists as jest.Mock).mockResolvedValue(false);

await expect(
service.importVectorCollection(mockClientMetadata, {
collectionName: 'bikes',
}),
).rejects.toThrow('No data file found for collection: bikes');

expect(mockedFs.pathExists).toHaveBeenCalledWith(
expect.stringContaining('vector-collections/bikes'),
);
});

it('should handle import errors', async () => {
const spy = jest.spyOn(service, 'import');
const importError = new Error('Import failed');
spy.mockRejectedValue(importError);

(mockedFs.pathExists as jest.Mock).mockResolvedValue(true);
(mockedFs.createReadStream as jest.Mock).mockReturnValue(new Readable());

await expect(
service.importVectorCollection(mockClientMetadata, {
collectionName: 'bikes',
}),
).rejects.toThrow('Import failed');

expect(spy).toHaveBeenCalledWith(
mockClientMetadata,
expect.any(Readable),
);
});

it('should throw BadRequestException when collectionName is not in allowed list', async () => {
await expect(
service.importVectorCollection(mockClientMetadata, {
collectionName: '../../etc/passwd', // malicious input
}),
).rejects.toThrow('Invalid collection name');
});
});
});
Loading
Loading