Skip to content

Commit 748507f

Browse files
authored
feat(handler): update chunk resource naming to AIP-compliant nested format (#327)
**Because** - Chunk resource names did not follow AIP nested resource naming conventions - gRPC integration tests were flaky due to transient errors without retry logic - File listing required a knowledge base, limiting flexibility for namespace-wide queries **This commit** - Update chunk resource name format from `namespaces/{ns}/files/{file}/chunks/{chunk}` to `namespaces/{ns}/knowledge-bases/{kb}/files/{file}/chunks/{chunk}` for AIP compliance - Update file resource name parsing to support both flat (`namespaces/{ns}/files/{file}`) and nested (`namespaces/{ns}/knowledge-bases/{kb}/files/{file}`) formats - Add validation for required knowledge base parameter in `SearchChunks` - Allow `ListFiles` to work at namespace level without requiring a knowledge base - Add `grpcInvokeWithRetry` helper for automatic gRPC retry with exponential backoff on transient errors (Unknown, DeadlineExceeded, ResourceExhausted, Aborted, Unavailable) - Update all 145 gRPC test invocations to use the retry helper for improved test stability - Update resource name validation in tests to support nested resource formats - Rename `collectionIds` to `knowledgeBases` in file validation
1 parent d462ad2 commit 748507f

26 files changed

+921
-954
lines changed

Dockerfile.dev

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
ARG GOLANG_VERSION=1.25.6
2-
FROM golang:${GOLANG_VERSION}-bullseye
2+
FROM golang:${GOLANG_VERSION}-bookworm
33

44
ARG SERVICE_NAME SERVICE_VERSION
55

@@ -36,7 +36,7 @@ RUN curl -fsSL https://dl.min.io/client/mc/release/linux-${TARGETARCH}/mc -o /us
3636
# Also install GNU parallel for running tests in parallel
3737
RUN apt-get update && apt-get install -y python3 python3-pip parallel && rm -rf /var/lib/apt/lists/*
3838
# Install pymilvus
39-
RUN pip3 install pymilvus>=2.4.3
39+
RUN pip3 install --break-system-packages "pymilvus>=2.6"
4040

4141
# -- set up Go
4242
COPY go.mod go.sum ./

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ require (
1313
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
1414
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1
1515
github.com/iancoleman/strcase v0.3.0
16-
github.com/instill-ai/protogen-go v0.3.3-alpha.0.20260129193003-3968ab7c42a9
16+
github.com/instill-ai/protogen-go v0.3.3-alpha.0.20260130101410-2df13d165253
1717
github.com/instill-ai/x v0.10.1-alpha.0.20260129195415-09e1680f104d
1818
github.com/knadh/koanf v1.5.0
1919
github.com/mennanov/fieldmask-utils v1.1.2

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -419,8 +419,8 @@ github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSAS
419419
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
420420
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
421421
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
422-
github.com/instill-ai/protogen-go v0.3.3-alpha.0.20260129193003-3968ab7c42a9 h1:zOKahHOtvNkvUOLsM7HC2xThbjIx3THwP4+4hWwy/GA=
423-
github.com/instill-ai/protogen-go v0.3.3-alpha.0.20260129193003-3968ab7c42a9/go.mod h1:bCnBosofpaUxKBuTTJM3/I3thAK37kvfBnKByjnLsl4=
422+
github.com/instill-ai/protogen-go v0.3.3-alpha.0.20260130101410-2df13d165253 h1:PlMigeqj4xkVoONNMja/ppDlzWs+e0pai2mmxBzpIjg=
423+
github.com/instill-ai/protogen-go v0.3.3-alpha.0.20260130101410-2df13d165253/go.mod h1:bCnBosofpaUxKBuTTJM3/I3thAK37kvfBnKByjnLsl4=
424424
github.com/instill-ai/x v0.10.1-alpha.0.20260129195415-09e1680f104d h1:FLDqMpALmq2e/j9inTGWrY4mhRFhe5/xXvjjxwQDqDg=
425425
github.com/instill-ai/x v0.10.1-alpha.0.20260129195415-09e1680f104d/go.mod h1:/xG0LMh43kyyK9jXGtV/JGd5p9GVcreSZ6GRGVbALEU=
426426
github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI=

integration-test/const.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,22 @@ export const paramsHTTPWithJWT = {
7272
},
7373
};
7474

75+
// Invalid Basic Auth credentials for testing authentication rejection
76+
const invalidAuthHeader = `Basic ${encoding.b64encode("invalid:wrongpassword")}`;
77+
export const paramsGRPCWithInvalidAuth = {
78+
metadata: {
79+
"Content-Type": "application/json",
80+
"Authorization": invalidAuthHeader,
81+
},
82+
};
83+
84+
// No auth metadata for testing unauthenticated requests
85+
export const paramsGRPCNoAuth = {
86+
metadata: {
87+
"Content-Type": "application/json",
88+
},
89+
};
90+
7591
// Test document sample data files
7692
export const docSampleDoc = encoding.b64encode(
7793
open(`${__ENV.TEST_FOLDER_ABS_PATH}/integration-test/data/doc-sample.doc`, "b")

integration-test/grpc-kb-update.js

Lines changed: 118 additions & 132 deletions
Large diffs are not rendered by default.

integration-test/grpc-system-admin.js

Lines changed: 22 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import { check, group } from "k6";
3232

3333
import * as constant from "./const.js";
3434
import * as helper from "./helper.js";
35+
import { grpcInvokeWithRetry } from "./helper.js";
3536

3637
// Use httpRetry for automatic retry on transient errors (429, 5xx)
3738
const http = helper.httpRetry;
@@ -59,25 +60,10 @@ export function setup() {
5960
console.log(`grpc-system-admin.js: Using unique test prefix: ${dbIDPrefix}`);
6061

6162
// Authenticate with retry to handle transient failures
62-
const loginResp = helper.authenticateWithRetry(
63-
constant.mgmtRESTPublicHost,
64-
constant.defaultUsername,
65-
constant.defaultPassword
66-
);
67-
68-
check(loginResp, {
69-
"Setup: Authentication successful": (r) => r && r.status === 200,
70-
});
71-
72-
if (!loginResp || loginResp.status !== 200) {
73-
console.error("Setup: Authentication failed, cannot continue");
74-
return null;
75-
}
76-
77-
const accessToken = loginResp.json().accessToken;
63+
const authHeader = helper.getBasicAuthHeader(constant.defaultUsername, constant.defaultPassword);
7864
const grpcMetadata = {
7965
"metadata": {
80-
"Authorization": `Bearer ${accessToken}`,
66+
"Authorization": authHeader,
8167
},
8268
"timeout": "300s",
8369
};
@@ -100,7 +86,7 @@ export function teardown(data) {
10086

10187
for (const systemId of testSystemIds) {
10288
try {
103-
client.invoke(
89+
grpcInvokeWithRetry(client,
10490
"artifact.v1alpha.ArtifactPrivateService/DeleteSystemAdmin",
10591
{ system_id: systemId },
10692
data.metadata
@@ -133,7 +119,7 @@ export default function (data) {
133119
group("Phase 1: List all system configurations", () => {
134120
console.log("\n=== Phase 1: Listing all systems ===");
135121

136-
const listRes = client.invoke(
122+
const listRes = grpcInvokeWithRetry(client,
137123
"artifact.v1alpha.ArtifactPrivateService/ListSystemsAdmin",
138124
{},
139125
data.metadata
@@ -175,7 +161,7 @@ export default function (data) {
175161
console.log("Phase 2: OpenAI system ID not found in Phase 1, skipping OpenAI tests");
176162
} else {
177163
// Get OpenAI system using its canonical ID (sys-{hash} format)
178-
const openaiRes = client.invoke(
164+
const openaiRes = grpcInvokeWithRetry(client,
179165
"artifact.v1alpha.ArtifactPrivateService/GetSystemAdmin",
180166
{ system_id: openaiSystemId },
181167
data.metadata
@@ -197,7 +183,7 @@ export default function (data) {
197183
console.log("Phase 2: Gemini system ID not found in Phase 1, skipping Gemini tests");
198184
} else {
199185
// Get Gemini system using its canonical ID
200-
const geminiRes = client.invoke(
186+
const geminiRes = grpcInvokeWithRetry(client,
201187
"artifact.v1alpha.ArtifactPrivateService/GetSystemAdmin",
202188
{ system_id: geminiSystemId },
203189
data.metadata
@@ -221,7 +207,7 @@ export default function (data) {
221207
group("Phase 3: Get default system configuration", () => {
222208
console.log("\n=== Phase 3: Getting default system ===");
223209

224-
const defaultRes = client.invoke(
210+
const defaultRes = grpcInvokeWithRetry(client,
225211
"artifact.v1alpha.ArtifactPrivateService/GetDefaultSystemAdmin",
226212
{},
227213
data.metadata
@@ -249,7 +235,7 @@ export default function (data) {
249235
// API CHANGE: ID is now auto-generated as "sys-{hash}", use display_name instead
250236
const customDisplayName = `${data.dbIDPrefix}sysadmin-custom`;
251237

252-
const createRes = client.invoke(
238+
const createRes = grpcInvokeWithRetry(client,
253239
"artifact.v1alpha.ArtifactPrivateService/CreateSystemAdmin",
254240
{
255241
system: {
@@ -310,7 +296,7 @@ export default function (data) {
310296
};
311297
console.log(`Phase 5: Request object: ${JSON.stringify(updateRequest)}`);
312298

313-
const updateRes = client.invoke(
299+
const updateRes = grpcInvokeWithRetry(client,
314300
"artifact.v1alpha.ArtifactPrivateService/UpdateSystemAdmin",
315301
updateRequest,
316302
data.metadata
@@ -333,7 +319,7 @@ export default function (data) {
333319
// Update both config and description
334320
console.log("\n=== Phase 5: Updating config with field mask ===");
335321

336-
const updateConfigRes = client.invoke(
322+
const updateConfigRes = grpcInvokeWithRetry(client,
337323
"artifact.v1alpha.ArtifactPrivateService/UpdateSystemAdmin",
338324
{
339325
system: {
@@ -384,7 +370,7 @@ export default function (data) {
384370
// API CHANGE: IDs are immutable, we can only change display_name (and slug)
385371
const newDisplayName = `${data.dbIDPrefix}sysadmin-renamed`;
386372

387-
const renameRes = client.invoke(
373+
const renameRes = grpcInvokeWithRetry(client,
388374
"artifact.v1alpha.ArtifactPrivateService/RenameSystemAdmin",
389375
{
390376
system_id: customSystemId,
@@ -410,7 +396,7 @@ export default function (data) {
410396
}
411397

412398
// Verify system is still accessible by the same ID (ID is immutable)
413-
const getRes = client.invoke(
399+
const getRes = grpcInvokeWithRetry(client,
414400
"artifact.v1alpha.ArtifactPrivateService/GetSystemAdmin",
415401
{ system_id: customSystemId },
416402
data.metadata
@@ -435,7 +421,7 @@ export default function (data) {
435421
}
436422

437423
// First, get current default
438-
const getCurrentDefaultRes = client.invoke(
424+
const getCurrentDefaultRes = grpcInvokeWithRetry(client,
439425
"artifact.v1alpha.ArtifactPrivateService/GetDefaultSystemAdmin",
440426
{},
441427
data.metadata
@@ -448,7 +434,7 @@ export default function (data) {
448434
}
449435

450436
// Set custom system as default
451-
const setDefaultRes = client.invoke(
437+
const setDefaultRes = grpcInvokeWithRetry(client,
452438
"artifact.v1alpha.ArtifactPrivateService/SetDefaultSystemAdmin",
453439
{ system_id: renamedSystemId },
454440
data.metadata
@@ -461,7 +447,7 @@ export default function (data) {
461447
});
462448

463449
// Verify via GetDefaultSystemAdmin
464-
const verifyDefaultRes = client.invoke(
450+
const verifyDefaultRes = grpcInvokeWithRetry(client,
465451
"artifact.v1alpha.ArtifactPrivateService/GetDefaultSystemAdmin",
466452
{},
467453
data.metadata
@@ -476,7 +462,7 @@ export default function (data) {
476462
// Restore original default
477463
if (previousDefaultId && previousDefaultId !== renamedSystemId) {
478464
console.log(`Phase 7: Restoring original default: ${previousDefaultId}`);
479-
const restoreRes = client.invoke(
465+
const restoreRes = grpcInvokeWithRetry(client,
480466
"artifact.v1alpha.ArtifactPrivateService/SetDefaultSystemAdmin",
481467
{ system_id: previousDefaultId },
482468
data.metadata
@@ -500,7 +486,7 @@ export default function (data) {
500486
return;
501487
}
502488

503-
const deleteRes = client.invoke(
489+
const deleteRes = grpcInvokeWithRetry(client,
504490
"artifact.v1alpha.ArtifactPrivateService/DeleteSystemAdmin",
505491
{ system_id: renamedSystemId },
506492
data.metadata
@@ -512,7 +498,7 @@ export default function (data) {
512498
});
513499

514500
// Verify system is deleted
515-
const getDeletedRes = client.invoke(
501+
const getDeletedRes = grpcInvokeWithRetry(client,
516502
"artifact.v1alpha.ArtifactPrivateService/GetSystemAdmin",
517503
{ system_id: renamedSystemId },
518504
data.metadata
@@ -542,7 +528,7 @@ export default function (data) {
542528
console.log(`Phase 9: Using OpenAI system ID: ${openaiSystemId}`);
543529

544530
// Try to delete openai system (should fail - protected)
545-
const deleteOpenaiRes = client.invoke(
531+
const deleteOpenaiRes = grpcInvokeWithRetry(client,
546532
"artifact.v1alpha.ArtifactPrivateService/DeleteSystemAdmin",
547533
{ system_id: openaiSystemId },
548534
data.metadata
@@ -553,7 +539,7 @@ export default function (data) {
553539
});
554540

555541
// Try to rename openai system (should fail - protected)
556-
const renameOpenaiRes = client.invoke(
542+
const renameOpenaiRes = grpcInvokeWithRetry(client,
557543
"artifact.v1alpha.ArtifactPrivateService/RenameSystemAdmin",
558544
{
559545
system_id: openaiSystemId,
@@ -567,7 +553,7 @@ export default function (data) {
567553
});
568554

569555
// Verify openai system still exists with same ID
570-
const getOpenaiRes = client.invoke(
556+
const getOpenaiRes = grpcInvokeWithRetry(client,
571557
"artifact.v1alpha.ArtifactPrivateService/GetSystemAdmin",
572558
{ system_id: openaiSystemId },
573559
data.metadata

0 commit comments

Comments
 (0)