Skip to content

Commit 423ba1d

Browse files
committed
refactor(publish): remove 'entity' and 'env' fields from records and update validation logic
1 parent eed3b67 commit 423ba1d

13 files changed

Lines changed: 91 additions & 55 deletions

File tree

packages/ddb-publisher/src/__integration__/publish-action.integration.test.ts

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,46 +21,34 @@ const bundlePath = resolve(
2121
);
2222
const tableName = "supplier-config-it";
2323
const expectedRecords = [
24-
{
25-
pk: "ENTITY#channel",
26-
sk: "ID#letter",
27-
entity: "channel",
28-
id: "letter",
29-
},
3024
{
3125
pk: "ENTITY#volume-group",
3226
sk: "ID#vg-1",
33-
entity: "volume-group",
3427
id: "vg-1",
3528
},
3629
{
3730
pk: "ENTITY#letter-variant",
3831
sk: "ID#lv-1",
39-
entity: "letter-variant",
4032
id: "lv-1",
4133
},
4234
{
4335
pk: "ENTITY#pack-specification",
4436
sk: "ID#pack-spec-1",
45-
entity: "pack-specification",
4637
id: "pack-spec-1",
4738
},
4839
{
4940
pk: "ENTITY#supplier",
5041
sk: "ID#sup-1",
51-
entity: "supplier",
5242
id: "sup-1",
5343
},
5444
{
5545
pk: "ENTITY#supplier-allocation",
5646
sk: "ID#alloc-1",
57-
entity: "supplier-allocation",
5847
id: "alloc-1",
5948
},
6049
{
6150
pk: "ENTITY#supplier-pack",
6251
sk: "ID#sp-1",
63-
entity: "supplier-pack",
6452
id: "sp-1",
6553
},
6654
] as const;
@@ -125,8 +113,6 @@ function expectExpectedRecords(
125113
);
126114

127115
expect(actual).toBeDefined();
128-
expect(actual?.env?.S).toBe("draft");
129-
expect(actual?.entity?.S).toBe(expectedRecord.entity);
130116
expect(actual?.id?.S).toBe(expectedRecord.id);
131117
}
132118
}
@@ -187,9 +173,7 @@ describe("publish action integration (DynamoDB Local)", () => {
187173
Item: {
188174
pk: { S: "ENTITY#supplier" },
189175
sk: { S: "ID#stale-supplier" },
190-
entity: { S: "supplier" },
191176
id: { S: "stale-supplier" },
192-
env: { S: "draft" },
193177
status: { S: "DRAFT" },
194178
name: { S: "Stale Supplier" },
195179
channelType: { S: "LETTER" },

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

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ function makeRecords(count: number): ConfigRecord[] {
77
entity: "supplier",
88
sourceFilePath: `/tmp/supplier/${i}.json`,
99
id: String(i),
10-
data: { ok: true },
10+
data: { id: String(i), ok: true },
1111
}));
1212
}
1313

@@ -42,7 +42,6 @@ describe("publishRecords", () => {
4242
await publishRecords({
4343
ddb: { send } as any,
4444
tableName: "tbl",
45-
env: "draft",
4645
records: [],
4746
});
4847

@@ -55,7 +54,6 @@ describe("publishRecords", () => {
5554
await publishRecords({
5655
ddb: ddb as any,
5756
tableName: "tbl",
58-
env: "draft",
5957
records: makeRecords(26),
6058
});
6159

@@ -70,13 +68,13 @@ describe("publishRecords", () => {
7068
await publishRecords({
7169
ddb: ddb as any,
7270
tableName: "tbl",
73-
env: "draft",
7471
records: [
7572
{
7673
entity: "supplier",
7774
sourceFilePath: "/tmp/supplier/sup-1.json",
7875
id: "sup-1",
7976
data: {
77+
id: "sup-1",
8078
name: "Example Supplier",
8179
status: "DRAFT",
8280
dailyCapacity: 100,
@@ -91,9 +89,7 @@ describe("publishRecords", () => {
9189
Item: {
9290
pk: "ENTITY#supplier",
9391
sk: "ID#sup-1",
94-
entity: "supplier",
9592
id: "sup-1",
96-
env: "draft",
9793
name: "Example Supplier",
9894
status: "DRAFT",
9995
dailyCapacity: 100,
@@ -125,7 +121,6 @@ describe("publishRecords", () => {
125121
await publishRecords({
126122
ddb: ddb as any,
127123
tableName: "tbl",
128-
env: "draft",
129124
records: makeRecords(1),
130125
});
131126

@@ -147,9 +142,27 @@ describe("publishRecords", () => {
147142
publishRecords({
148143
ddb: ddb as any,
149144
tableName: "tbl",
150-
env: "draft",
151145
records: makeRecords(1),
152146
}),
153147
).rejects.toThrow("Failed to write");
154148
});
149+
150+
it("should reject records that are not objects with an id string", async () => {
151+
const { ddb } = fakeDdb();
152+
153+
await expect(
154+
publishRecords({
155+
ddb: ddb as any,
156+
tableName: "tbl",
157+
records: [
158+
{
159+
entity: "supplier",
160+
sourceFilePath: "/tmp/supplier/sup-1.json",
161+
id: "sup-1",
162+
data: "not-an-object",
163+
},
164+
],
165+
}),
166+
).rejects.toThrow();
167+
});
155168
});

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,8 @@ describe("runPublisher", () => {
192192
data: {},
193193
},
194194
{
195-
entity: "channel",
196-
sourceFilePath: "/tmp/channel/1.json",
195+
entity: "supplier-pack",
196+
sourceFilePath: "/tmp/supplier-pack/1.json",
197197
id: "1",
198198
data: {},
199199
},
@@ -218,7 +218,7 @@ describe("runPublisher", () => {
218218
});
219219

220220
const logOutput = writeSpy.mock.calls.map(([msg]) => String(msg)).join("\n");
221-
expect(logOutput).toContain("Loaded 3 records (channel=1, supplier=2).\n");
221+
expect(logOutput).toContain("Loaded 3 records (supplier=2, supplier-pack=1).\n");
222222

223223
writeSpy.mockRestore();
224224
});

packages/ddb-publisher/src/ddb/audit.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { AttributeValue } from "@aws-sdk/client-dynamodb";
2-
import { DynamoDBDocumentClient, ScanCommand } from "@aws-sdk/lib-dynamodb";
2+
import {DynamoDBDocumentClient, ScanCommand, ScanCommandOutput} from "@aws-sdk/lib-dynamodb";
33
import { z } from "zod";
44

55
import type { ConfigRecord } from "@supplier-config/file-store";
@@ -45,7 +45,7 @@ async function auditBeforeLoad(params: {
4545

4646
// Scan all records in the table (projecting only keys used for validation)
4747
do {
48-
const page = await params.ddb.send(
48+
const page: ScanCommandOutput = await params.ddb.send(
4949
new ScanCommand({
5050
TableName: params.tableName,
5151
ExclusiveStartKey: lastEvaluatedKey ?? undefined,

packages/ddb-publisher/src/ddb/publish.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,27 @@
11
import {BatchWriteCommand, DynamoDBDocumentClient} from "@aws-sdk/lib-dynamodb";
2+
import {z} from "zod";
23

34
import type {ConfigRecord} from "@supplier-config/file-store";
45

56
import {pkForEntity, skForId} from "./keys";
67

7-
function itemForRecord(record: ConfigRecord, env: string): Record<string, unknown> {
8-
const base = record.data as Record<string, unknown>;
8+
const persistedRecordShapeSchema = z.looseObject({
9+
id: z.string(),
10+
});
11+
12+
function itemForRecord(record: ConfigRecord): Record<string, unknown> {
13+
const base = persistedRecordShapeSchema.parse(record.data);
914

1015
return {
1116
pk: pkForEntity(record.entity),
1217
sk: skForId(record.id),
13-
entity: record.entity,
14-
id: record.id,
15-
env,
1618
...base,
1719
};
1820
}
1921

2022
export async function publishRecords(params: {
2123
ddb: DynamoDBDocumentClient;
2224
tableName: string;
23-
env: string;
2425
records: ConfigRecord[];
2526
}): Promise<void> {
2627
const chunks: ConfigRecord[][] = [];
@@ -31,7 +32,7 @@ export async function publishRecords(params: {
3132
for (const chunk of chunks) {
3233
let unprocessed = chunk.map((r) => ({
3334
PutRequest: {
34-
Item: itemForRecord(r, params.env),
35+
Item: itemForRecord(r),
3536
},
3637
}));
3738

packages/ddb-publisher/src/run.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,6 @@ async function runPublisher(plan: LoadPlan): Promise<void> {
145145
await publishRecords({
146146
ddb,
147147
tableName: plan.tableName,
148-
env: plan.env,
149148
records: store.records,
150149
});
151150

packages/file-store/src/__tests__/config-store-loader.test.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,8 @@ describe("loadConfigStore", () => {
1818
const store = await loadConfigStore(exampleConfigStoreRoot);
1919

2020
expect(store.rootPath).toBe(exampleConfigStoreRoot);
21-
expect(store.records).toHaveLength(7);
21+
expect(store.records).toHaveLength(6);
2222
expect(store.records.map((record) => `${record.entity}:${record.id}`).sort()).toEqual([
23-
"channel:letter",
2423
"letter-variant:lv-1",
2524
"pack-specification:pack-spec-1",
2625
"supplier-allocation:alloc-1",
@@ -30,11 +29,12 @@ describe("loadConfigStore", () => {
3029
]);
3130
});
3231

33-
it("should ignore non-json files and unexpected directories", async () => {
32+
it("should ignore non-json files and non-persisted directories", async () => {
3433
const root = await createTempConfigStore();
3534

3635
try {
3736
await mkdir(join(root, "supplier"), { recursive: true });
37+
await mkdir(join(root, "channel"), { recursive: true });
3838
await mkdir(join(root, "unexpected-folder"), { recursive: true });
3939
await writeFile(
4040
join(root, "supplier", "sup-1.json"),
@@ -47,6 +47,11 @@ describe("loadConfigStore", () => {
4747
JSON.stringify({ ignored: true }),
4848
"utf8",
4949
);
50+
await writeFile(
51+
join(root, "channel", "letter.json"),
52+
JSON.stringify("LETTER"),
53+
"utf8",
54+
);
5055

5156
const store = await loadConfigStore(root);
5257

packages/file-store/src/__tests__/config-store-validator.test.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import {
88

99
jest.mock("@nhsdigital/nhs-notify-event-schemas-supplier-config", () => {
1010
return {
11-
$ChannelType: z.literal("LETTER"),
1211
$VolumeGroup: z.object({
1312
id: z.string(),
1413
name: z.string(),
@@ -57,7 +56,6 @@ describe("normalizeIssuePath", () => {
5756
describe("validateConfigStore", () => {
5857
it("should return ok=true with no issues for a larger mixed valid dataset", () => {
5958
const validRecords = [
60-
makeRecord("channel", "channel-1", "LETTER"),
6159
makeRecord("volume-group", "vg-1", {
6260
id: "vg-1",
6361
name: "VG 1",
@@ -177,4 +175,24 @@ describe("validateConfigStore", () => {
177175
expect(result.ok).toBe(true);
178176
expect(result.issues).toHaveLength(0);
179177
});
178+
179+
it("should report an issue when the filename id does not match the parsed object id", () => {
180+
const result = validateConfigStore({
181+
rootPath: "/tmp",
182+
records: [
183+
makeRecord("supplier", "sup-1", {
184+
id: "different-id",
185+
dailyCapacity: 100,
186+
}),
187+
],
188+
});
189+
190+
expect(result.ok).toBe(false);
191+
expect(result.issues).toContainEqual({
192+
entity: "supplier",
193+
sourceFilePath: "/tmp/supplier/sup-1.json",
194+
message: "Record id 'different-id' does not match filename id 'sup-1'.",
195+
path: ["id"],
196+
});
197+
});
180198
});

packages/file-store/src/loader/config-store-loader.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ import type {
77
LoadedConfigStore,
88
} from "../types.js";
99

10-
const entityDirectoryAllowList: readonly DomainEntityName[] = [
11-
"channel",
10+
const persistedEntityDirectoryAllowList: readonly DomainEntityName[] = [
1211
"volume-group",
1312
"letter-variant",
1413
"pack-specification",
@@ -17,8 +16,8 @@ const entityDirectoryAllowList: readonly DomainEntityName[] = [
1716
"supplier-pack",
1817
];
1918

20-
function isEntityDirName(name: string): name is DomainEntityName {
21-
return (entityDirectoryAllowList as readonly string[]).includes(name);
19+
function isPersistedEntityDirName(name: string): name is DomainEntityName {
20+
return (persistedEntityDirectoryAllowList as readonly string[]).includes(name);
2221
}
2322

2423
/**
@@ -44,7 +43,7 @@ export async function loadConfigStore(
4443
const records: ConfigRecord[] = [];
4544

4645
for (const entry of dirents) {
47-
if (!isEntityDirName(entry)) continue;
46+
if (!isPersistedEntityDirName(entry)) continue;
4847

4948
const entityDir = join(resolvedRoot, entry);
5049
const files = await readdir(entityDir);

packages/file-store/src/types.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import type { z } from 'zod';
22

33
export type DomainEntityName =
4-
| 'channel'
54
| 'volume-group'
65
| 'letter-variant'
76
| 'pack-specification'

0 commit comments

Comments
 (0)