Skip to content

Commit e90f2c1

Browse files
authored
Add file resource wrappers (#579)
1 parent eedaf98 commit e90f2c1

3 files changed

Lines changed: 193 additions & 1 deletion

File tree

milvus/grpc/GrpcClient.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ import {
2929
DEFAULT_POOL_MIN,
3030
RunAnalyzerRequest,
3131
RunAnalyzerResponse,
32+
ResStatus,
33+
AddFileResourceReq,
34+
RemoveFileResourceReq,
35+
ListFileResourcesReq,
36+
ListFileResourcesResponse,
3237
fetchTopology,
3338
getPrimaryCluster,
3439
TopologyRefresher,
@@ -495,4 +500,53 @@ export class GRPCClient extends User {
495500
this.timeout
496501
);
497502
}
503+
504+
/**
505+
* Adds a file resource to Milvus metadata.
506+
* @param {AddFileResourceReq} data - The file resource name and server-visible path.
507+
* @returns {Promise<ResStatus>} - A Promise that resolves with the operation status.
508+
*/
509+
async addFileResource(data: AddFileResourceReq): Promise<ResStatus> {
510+
return await promisify(
511+
this.channelPool,
512+
'AddFileResource',
513+
{
514+
name: data.name,
515+
path: data.path,
516+
},
517+
data.timeout || this.timeout
518+
);
519+
}
520+
521+
/**
522+
* Removes a file resource from Milvus metadata.
523+
* @param {RemoveFileResourceReq} data - The file resource name.
524+
* @returns {Promise<ResStatus>} - A Promise that resolves with the operation status.
525+
*/
526+
async removeFileResource(data: RemoveFileResourceReq): Promise<ResStatus> {
527+
return await promisify(
528+
this.channelPool,
529+
'RemoveFileResource',
530+
{
531+
name: data.name,
532+
},
533+
data.timeout || this.timeout
534+
);
535+
}
536+
537+
/**
538+
* Lists file resources registered in Milvus metadata.
539+
* @param {ListFileResourcesReq} [data] - Optional request parameters.
540+
* @returns {Promise<ListFileResourcesResponse>} - A Promise that resolves with file resources.
541+
*/
542+
async listFileResources(
543+
data: ListFileResourcesReq = {}
544+
): Promise<ListFileResourcesResponse> {
545+
return await promisify(
546+
this.channelPool,
547+
'ListFileResources',
548+
{},
549+
data.timeout || this.timeout
550+
);
551+
}
498552
}

milvus/types/Client.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ChannelOptions } from '@grpc/grpc-js';
22
import { Options as LoaderOption } from '@grpc/proto-loader';
33
import { Options } from 'generic-pool';
4-
import { ResStatus } from './';
4+
import { GrpcTimeOut, ResStatus } from './Common';
55

66
/**
77
* Configuration options for the Milvus client.
@@ -114,3 +114,25 @@ export interface RunAnalyzerResponse {
114114
status: ResStatus;
115115
results: AnalyzerResult[];
116116
}
117+
118+
export interface AddFileResourceReq extends GrpcTimeOut {
119+
name: string;
120+
path: string;
121+
}
122+
123+
export interface RemoveFileResourceReq extends GrpcTimeOut {
124+
name: string;
125+
}
126+
127+
export interface ListFileResourcesReq extends GrpcTimeOut {}
128+
129+
export interface FileResourceInfo {
130+
id: string;
131+
name: string;
132+
path: string;
133+
}
134+
135+
export interface ListFileResourcesResponse {
136+
status: ResStatus;
137+
resources: FileResourceInfo[];
138+
}

test/grpc/FileResource.spec.ts

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import { Client as MinioClient } from 'minio';
2+
import { execSync } from 'child_process';
3+
import { ErrorCode, MilvusClient } from '../../milvus';
4+
import { GENERATE_NAME, IP } from '../tools';
5+
6+
const milvusClient = new MilvusClient({ address: IP, logLevel: 'info' });
7+
let minioClient: MinioClient;
8+
9+
const BUCKET_NAME = 'a-bucket';
10+
const RESOURCE_NAME = GENERATE_NAME('file_resource');
11+
const MISSING_RESOURCE_NAME = GENERATE_NAME('missing_file_resource');
12+
const OBJECT_NAME = `node-sdk-file-resource/${RESOURCE_NAME}.txt`;
13+
const MISSING_OBJECT_NAME = `node-sdk-file-resource/${MISSING_RESOURCE_NAME}.txt`;
14+
15+
const createMinioClient = (endPoint: string) =>
16+
new MinioClient({
17+
endPoint,
18+
port: 9000,
19+
useSSL: false,
20+
accessKey: 'minioadmin',
21+
secretKey: 'minioadmin',
22+
});
23+
24+
const getMilvusMinioContainerIP = () =>
25+
execSync(
26+
`docker inspect milvus-minio --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'`,
27+
{ encoding: 'utf8' }
28+
).trim();
29+
30+
const putTestObject = async () => {
31+
const candidates = [
32+
() => process.env.FILE_RESOURCE_MINIO_ENDPOINT || '127.0.0.1',
33+
getMilvusMinioContainerIP,
34+
];
35+
36+
let lastError: unknown;
37+
for (const getEndPoint of candidates) {
38+
try {
39+
const endPoint = getEndPoint();
40+
minioClient = createMinioClient(endPoint);
41+
await minioClient.putObject(
42+
BUCKET_NAME,
43+
OBJECT_NAME,
44+
Buffer.from('node sdk file resource test\n')
45+
);
46+
return;
47+
} catch (error) {
48+
lastError = error;
49+
}
50+
}
51+
52+
throw lastError;
53+
};
54+
55+
describe('FileResource API', () => {
56+
beforeAll(async () => {
57+
await putTestObject();
58+
});
59+
60+
afterAll(async () => {
61+
try {
62+
await milvusClient.removeFileResource({ name: RESOURCE_NAME });
63+
} catch {
64+
// best-effort cleanup
65+
}
66+
67+
try {
68+
await minioClient.removeObject(BUCKET_NAME, OBJECT_NAME);
69+
} catch {
70+
// best-effort cleanup
71+
}
72+
});
73+
74+
it('should list file resources', async () => {
75+
const res = await milvusClient.listFileResources();
76+
77+
expect(res.status.error_code).toEqual(ErrorCode.SUCCESS);
78+
expect(Array.isArray(res.resources)).toEqual(true);
79+
});
80+
81+
it('should reject add file resource with missing object path', async () => {
82+
const res = await milvusClient.addFileResource({
83+
name: MISSING_RESOURCE_NAME,
84+
path: MISSING_OBJECT_NAME,
85+
});
86+
87+
expect(res.error_code).not.toEqual(ErrorCode.SUCCESS);
88+
});
89+
90+
it('should add, list, and remove file resource', async () => {
91+
const add = await milvusClient.addFileResource({
92+
name: RESOURCE_NAME,
93+
path: OBJECT_NAME,
94+
timeout: 10000,
95+
});
96+
expect(add.error_code).toEqual(ErrorCode.SUCCESS);
97+
98+
const list = await milvusClient.listFileResources({ timeout: 10000 });
99+
expect(list.status.error_code).toEqual(ErrorCode.SUCCESS);
100+
101+
const resource = list.resources.find(item => item.name === RESOURCE_NAME);
102+
expect(resource).toBeDefined();
103+
expect(resource!.path).toEqual(OBJECT_NAME);
104+
105+
const remove = await milvusClient.removeFileResource({
106+
name: RESOURCE_NAME,
107+
timeout: 10000,
108+
});
109+
expect(remove.error_code).toEqual(ErrorCode.SUCCESS);
110+
111+
const listAfterRemove = await milvusClient.listFileResources();
112+
expect(
113+
listAfterRemove.resources.some(item => item.name === RESOURCE_NAME)
114+
).toEqual(false);
115+
});
116+
});

0 commit comments

Comments
 (0)