Skip to content

Commit 7f5f033

Browse files
committed
fix(webapp): stop logging full batch item contents in batchTriggerV3
The idempotency grouping step logged every batch item verbatim, including its payload and options (which can carry arbitrary customer metadata). Replace that with a summary of task identifiers and item counts, and add a unit test covering the summary shape.
1 parent ec562c0 commit 7f5f033

3 files changed

Lines changed: 69 additions & 3 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
area: webapp
3+
type: fix
4+
---
5+
6+
Batch triggering with idempotency keys no longer logs the full contents of each batch item (including any metadata you attached). Logs now only include item counts and task names.

apps/webapp/app/v3/services/batchTriggerV3.server.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,27 @@ function chunkArray<T>(items: T[], size: number): T[][] {
4343
}
4444
return chunks;
4545
}
46+
47+
// Summarizes a task-identifier -> items grouping for logging, without including the items
48+
// themselves (each item is `{ task, payload, options }`, and `options.metadata` is arbitrary
49+
// customer data that must never be written to logs whole).
50+
export function summarizeItemsByTask(
51+
itemsByTask: Record<string, unknown[]>
52+
): { taskIdentifiers: string[]; itemCountsByTask: Record<string, number>; totalItemCount: number } {
53+
const itemCountsByTask: Record<string, number> = {};
54+
let totalItemCount = 0;
55+
56+
for (const [taskIdentifier, items] of Object.entries(itemsByTask)) {
57+
itemCountsByTask[taskIdentifier] = items.length;
58+
totalItemCount += items.length;
59+
}
60+
61+
return {
62+
taskIdentifiers: Object.keys(itemsByTask),
63+
itemCountsByTask,
64+
totalItemCount,
65+
};
66+
}
4667
const ASYNC_BATCH_PROCESS_SIZE_THRESHOLD = 20;
4768
const MAX_ATTEMPTS = 10;
4869

@@ -409,9 +430,10 @@ export class BatchTriggerV3Service extends BaseService {
409430
{} as Record<string, typeof body.items>
410431
);
411432

412-
logger.debug("[BatchTriggerV2][call] Grouped items by task identifier", {
413-
itemsByTask,
414-
});
433+
logger.debug(
434+
"[BatchTriggerV2][call] Grouped items by task identifier",
435+
summarizeItemsByTask(itemsByTask)
436+
);
415437

416438
const idempotencyKeyLookups = Object.entries(itemsByTask).flatMap(([taskIdentifier, items]) => {
417439
const idempotencyKeys = Array.from(
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { describe, expect, test } from "vitest";
2+
import { summarizeItemsByTask } from "~/v3/services/batchTriggerV3.server";
3+
4+
describe("summarizeItemsByTask", () => {
5+
test("returns counts and task identifiers without the underlying items", () => {
6+
const itemsByTask = {
7+
"my-task": [
8+
{ task: "my-task", payload: { secret: "value" }, options: { metadata: { pii: "yes" } } },
9+
{ task: "my-task", payload: { secret: "value-2" }, options: {} },
10+
],
11+
"other-task": [{ task: "other-task", payload: {}, options: {} }],
12+
};
13+
14+
const summary = summarizeItemsByTask(itemsByTask);
15+
16+
expect(summary).toEqual({
17+
taskIdentifiers: ["my-task", "other-task"],
18+
itemCountsByTask: { "my-task": 2, "other-task": 1 },
19+
totalItemCount: 3,
20+
});
21+
22+
// The summary must never contain the raw items, their payloads, or their options -
23+
// just identifiers and counts.
24+
const serialized = JSON.stringify(summary);
25+
expect(serialized).not.toContain("payload");
26+
expect(serialized).not.toContain("secret");
27+
expect(serialized).not.toContain("metadata");
28+
expect(serialized).not.toContain("pii");
29+
});
30+
31+
test("returns empty collections when there are no idempotent items", () => {
32+
expect(summarizeItemsByTask({})).toEqual({
33+
taskIdentifiers: [],
34+
itemCountsByTask: {},
35+
totalItemCount: 0,
36+
});
37+
});
38+
});

0 commit comments

Comments
 (0)