Skip to content

Commit daecddc

Browse files
committed
Enhance runPublisher to log detailed steps and summarize loaded records
1 parent 70ff42e commit daecddc

File tree

2 files changed

+117
-2
lines changed

2 files changed

+117
-2
lines changed

packages/ddb-publisher/src/__tests__/run.test.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,4 +149,73 @@ describe("runPublisher", () => {
149149

150150
expect(publish.publishRecords).toHaveBeenCalledTimes(1);
151151
});
152+
153+
it("should publish when audit reports no blocking items", async () => {
154+
const store: LoadedConfigStore = {
155+
rootPath: "/tmp",
156+
records: [],
157+
};
158+
fileStore.loadConfigStore.mockResolvedValue(store);
159+
fileStore.validateConfigStore.mockReturnValue({ ok: true, issues: [] });
160+
161+
audit.auditBeforeLoad.mockResolvedValue({
162+
blocking: [],
163+
});
164+
165+
await runPublisher({
166+
sourcePath: "/tmp",
167+
env: "draft",
168+
tableName: "tbl",
169+
dryRun: false,
170+
force: false,
171+
});
172+
173+
expect(publish.publishRecords).toHaveBeenCalledTimes(1);
174+
});
175+
176+
it("should log entity summary when records are loaded", async () => {
177+
const writeSpy = jest
178+
.spyOn(process.stdout, "write")
179+
.mockImplementation(() => true);
180+
181+
const store: LoadedConfigStore = {
182+
rootPath: "/tmp",
183+
records: [
184+
{
185+
entity: "supplier",
186+
sourceFilePath: "/tmp/supplier/1.json",
187+
id: "1",
188+
data: {},
189+
},
190+
{
191+
entity: "channel",
192+
sourceFilePath: "/tmp/channel/1.json",
193+
id: "1",
194+
data: {},
195+
},
196+
{
197+
entity: "supplier",
198+
sourceFilePath: "/tmp/supplier/2.json",
199+
id: "2",
200+
data: {},
201+
},
202+
],
203+
};
204+
205+
fileStore.loadConfigStore.mockResolvedValue(store);
206+
fileStore.validateConfigStore.mockReturnValue({ ok: true, issues: [] });
207+
208+
await runPublisher({
209+
sourcePath: "/tmp",
210+
env: "draft",
211+
tableName: "tbl",
212+
dryRun: true,
213+
force: false,
214+
});
215+
216+
const logOutput = writeSpy.mock.calls.map(([msg]) => String(msg)).join("\n");
217+
expect(logOutput).toContain("Loaded 3 records (channel=1, supplier=2).\n");
218+
219+
writeSpy.mockRestore();
220+
});
152221
});

packages/ddb-publisher/src/run.ts

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
loadConfigStore,
66
validateConfigStore,
77
} from "@supplier-config/file-store";
8-
import type { ValidationIssue } from "@supplier-config/file-store";
8+
import type { ConfigRecord, ValidationIssue } from "@supplier-config/file-store";
99

1010
import { auditBeforeLoad } from "./ddb/audit";
1111
import { publishRecords } from "./ddb/publish";
@@ -21,11 +21,40 @@ function issueLabel(i: {
2121
return `${i.entity} ${i.sourceFilePath}${pathPart} - ${i.message}`;
2222
}
2323

24+
function logStep(message: string): void {
25+
process.stdout.write(`[ddb-publisher] ${message}\n`);
26+
}
27+
28+
function summarizeEntities(records: ConfigRecord[]): string {
29+
const counts = new Map<string, number>();
30+
31+
for (const r of records) {
32+
counts.set(r.entity, (counts.get(r.entity) ?? 0) + 1);
33+
}
34+
35+
return [...counts.entries()]
36+
.sort(([a], [b]) => a.localeCompare(b))
37+
.map(([entity, count]) => `${entity}=${count}`)
38+
.join(", ");
39+
}
40+
2441
async function runPublisher(plan: LoadPlan): Promise<void> {
42+
logStep(
43+
`Starting publish run source='${plan.sourcePath}' env='${plan.env}' table='${plan.tableName}' force=${plan.force} dryRun=${plan.dryRun}`,
44+
);
45+
46+
logStep("Loading config store from disk...");
2547
const store = await loadConfigStore(plan.sourcePath);
48+
logStep(
49+
`Loaded ${store.records.length} records${store.records.length > 0 ? ` (${summarizeEntities(store.records)})` : ""}.`,
50+
);
51+
52+
logStep("Validating loaded records against schemas...");
2653
const validation = validateConfigStore(store);
2754

2855
if (!validation.ok) {
56+
logStep(`Validation failed with ${validation.issues.length} issue(s).`);
57+
2958
const summary = validation.issues
3059
.slice(0, 20)
3160
.map((i: ValidationIssue) => issueLabel(i))
@@ -36,22 +65,32 @@ async function runPublisher(plan: LoadPlan): Promise<void> {
3665
);
3766
}
3867

39-
if (plan.dryRun) return;
68+
logStep("Validation passed.");
69+
70+
if (plan.dryRun) {
71+
logStep("Dry-run enabled; skipping DynamoDB audit and publish.");
72+
return;
73+
}
4074

75+
logStep("Initialising DynamoDB client...");
4176
const client = new DynamoDBClient({});
4277
const ddb = DynamoDBDocumentClient.from(client, {
4378
marshallOptions: {
4479
removeUndefinedValues: true,
4580
},
4681
});
4782

83+
logStep("Auditing existing DynamoDB records...");
4884
const audit = await auditBeforeLoad({
4985
ddb,
5086
tableName: plan.tableName,
5187
localRecords: store.records,
5288
});
89+
logStep(`Audit completed. blockingRecords=${audit.blocking.length}`);
5390

5491
if (audit.blocking.length > 0 && !plan.force) {
92+
logStep("Blocking records found and --force not set; aborting publish.");
93+
5594
const examples = audit.blocking
5695
.slice(0, 20)
5796
.map(
@@ -65,12 +104,19 @@ async function runPublisher(plan: LoadPlan): Promise<void> {
65104
);
66105
}
67106

107+
if (audit.blocking.length > 0 && plan.force) {
108+
logStep("Blocking records found but --force enabled; continuing.");
109+
}
110+
111+
logStep(`Publishing ${store.records.length} record(s) to DynamoDB...`);
68112
await publishRecords({
69113
ddb,
70114
tableName: plan.tableName,
71115
env: plan.env,
72116
records: store.records,
73117
});
118+
119+
logStep("Publish completed successfully.");
74120
}
75121

76122
export default runPublisher;

0 commit comments

Comments
 (0)