Skip to content

Commit 731605a

Browse files
fix: handle missing metadata keys in CEL resource filter (#960)
1 parent a6f9c3b commit 731605a

2 files changed

Lines changed: 51 additions & 26 deletions

File tree

apps/api/src/routes/v1/workspaces/resources.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,11 @@ const listResources: AsyncTypedHandler<
4949

5050
const filteredResources = allResources.filter((resource) => {
5151
if (cel == null) return true;
52-
const matches = evaluate(cel, { resource });
53-
return matches;
52+
try {
53+
return evaluate(cel, { resource });
54+
} catch {
55+
return false;
56+
}
5457
});
5558

5659
const total = filteredResources.length;

e2e/tests/api/resources.spec.ts

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -288,9 +288,7 @@ test.describe("Resource API", () => {
288288
api,
289289
workspace,
290290
}) => {
291-
const suffix = faker.string.alphanumeric(8);
292-
const identifier = `res-plus-${suffix}`;
293-
const kind = `Kind+Plus-${suffix}`;
291+
const identifier = `res-plus-${faker.string.alphanumeric(8)}`;
294292

295293
await api.PUT(
296294
"/v1/workspaces/{workspaceId}/resources/identifier/{identifier}",
@@ -300,18 +298,18 @@ test.describe("Resource API", () => {
300298
},
301299
body: {
302300
name: "Plus Resource",
303-
kind,
301+
kind: "TestKind",
304302
version: "1.0.0",
305303
config: {},
306-
metadata: {},
304+
metadata: { tag: "a+b" },
307305
},
308306
},
309307
);
310308

311309
const listRes = await api.GET("/v1/workspaces/{workspaceId}/resources", {
312310
params: {
313311
path: { workspaceId: workspace.id },
314-
query: { cel: `resource.kind == "${kind}"` },
312+
query: { cel: 'resource.metadata["tag"] == "a+b"' },
315313
},
316314
});
317315

@@ -374,23 +372,38 @@ test.describe("Resource API", () => {
374372
);
375373
});
376374

377-
test("should filter resources with CEL containing literal %20", async ({
375+
test("should skip resources where CEL references a missing metadata key", async ({
378376
api,
379377
workspace,
380378
}) => {
381-
const suffix = faker.string.alphanumeric(8);
382-
const identifier = `res-pct20-${suffix}`;
383-
const kind = `Kind%20Space-${suffix}`;
379+
const identifier1 = `res-haskey-${faker.string.alphanumeric(8)}`;
380+
const identifier2 = `res-nokey-${faker.string.alphanumeric(8)}`;
384381

385382
await api.PUT(
386383
"/v1/workspaces/{workspaceId}/resources/identifier/{identifier}",
387384
{
388385
params: {
389-
path: { workspaceId: workspace.id, identifier },
386+
path: { workspaceId: workspace.id, identifier: identifier1 },
390387
},
391388
body: {
392-
name: "Pct20 Resource",
393-
kind,
389+
name: "Has Key",
390+
kind: "MissingKeyTest",
391+
version: "1.0.0",
392+
config: {},
393+
metadata: { env: "production" },
394+
},
395+
},
396+
);
397+
398+
await api.PUT(
399+
"/v1/workspaces/{workspaceId}/resources/identifier/{identifier}",
400+
{
401+
params: {
402+
path: { workspaceId: workspace.id, identifier: identifier2 },
403+
},
404+
body: {
405+
name: "No Key",
406+
kind: "MissingKeyTest",
394407
version: "1.0.0",
395408
config: {},
396409
metadata: {},
@@ -401,20 +414,31 @@ test.describe("Resource API", () => {
401414
const listRes = await api.GET("/v1/workspaces/{workspaceId}/resources", {
402415
params: {
403416
path: { workspaceId: workspace.id },
404-
query: { cel: `resource.kind == "${kind}"` },
417+
query: { cel: 'resource.metadata["env"] == "production"' },
405418
},
406419
});
407420

408421
expect(listRes.response.status).toBe(200);
409-
expect(listRes.data!.items.some((r) => r.identifier === identifier)).toBe(
410-
true,
411-
);
422+
expect(
423+
listRes.data!.items.some((r) => r.identifier === identifier1),
424+
).toBe(true);
425+
expect(
426+
listRes.data!.items.some((r) => r.identifier === identifier2),
427+
).toBe(false);
412428

413429
await api.DELETE(
414430
"/v1/workspaces/{workspaceId}/resources/identifier/{identifier}",
415431
{
416432
params: {
417-
path: { workspaceId: workspace.id, identifier },
433+
path: { workspaceId: workspace.id, identifier: identifier1 },
434+
},
435+
},
436+
);
437+
await api.DELETE(
438+
"/v1/workspaces/{workspaceId}/resources/identifier/{identifier}",
439+
{
440+
params: {
441+
path: { workspaceId: workspace.id, identifier: identifier2 },
418442
},
419443
},
420444
);
@@ -424,9 +448,7 @@ test.describe("Resource API", () => {
424448
api,
425449
workspace,
426450
}) => {
427-
const suffix = faker.string.alphanumeric(8);
428-
const identifier = `res-amp-${suffix}`;
429-
const name = `a&b=c-${suffix}`;
451+
const identifier = `res-amp-${faker.string.alphanumeric(8)}`;
430452

431453
await api.PUT(
432454
"/v1/workspaces/{workspaceId}/resources/identifier/{identifier}",
@@ -435,19 +457,19 @@ test.describe("Resource API", () => {
435457
path: { workspaceId: workspace.id, identifier },
436458
},
437459
body: {
438-
name,
460+
name: "Amp Resource",
439461
kind: "TestKind",
440462
version: "1.0.0",
441463
config: {},
442-
metadata: {},
464+
metadata: { "a&b": "x=y" },
443465
},
444466
},
445467
);
446468

447469
const listRes = await api.GET("/v1/workspaces/{workspaceId}/resources", {
448470
params: {
449471
path: { workspaceId: workspace.id },
450-
query: { cel: `resource.name == "${name}"` },
472+
query: { cel: 'resource.metadata["a&b"] == "x=y"' },
451473
},
452474
});
453475

0 commit comments

Comments
 (0)