Skip to content

Commit 91fa619

Browse files
committed
Add metrics
1 parent 30b613b commit 91fa619

4 files changed

Lines changed: 98 additions & 1 deletion

File tree

lambdas/supplier-config-ingress/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"@internal/helpers": "^0.1.0",
77
"@nhsdigital/nhs-notify-event-schemas-supplier-config": "^1.1.0",
88
"@types/aws-lambda": "^8.10.148",
9+
"aws-embedded-metrics": "^4.2.0",
910
"esbuild": "^0.27.2",
1011
"pino": "^9.7.0",
1112
"zod": "^4.1.11"

lambdas/supplier-config-ingress/src/__tests__/supplier-config-ingress-handler.test.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { SQSEvent, SQSRecord } from "aws-lambda";
2+
import { Unit } from "aws-embedded-metrics";
23
import createSupplierConfigIngressHandler from "../handler/supplier-config-ingress-handler";
34
import { Deps } from "../config/deps";
45

@@ -150,4 +151,65 @@ describe("supplierConfigHandler", () => {
150151
mockDeps.supplierConfigRepo.upsertSupplierConfig,
151152
).not.toHaveBeenCalled();
152153
});
154+
155+
it("emits metrics for created config events", async () => {
156+
const data = createSupplierConfig();
157+
const record = createSqsRecord(data);
158+
const event = { Records: [record] } as unknown as SQSEvent;
159+
160+
await handler(event);
161+
162+
expect(mockDeps.logger.info).toHaveBeenCalledWith(
163+
expect.objectContaining({
164+
entity: "supplier",
165+
result: "CREATED",
166+
ConfigEvents: 1,
167+
_aws: expect.objectContaining({
168+
CloudWatchMetrics: expect.arrayContaining([
169+
expect.objectContaining({
170+
Metrics: [
171+
expect.objectContaining({
172+
Name: "ConfigEvents",
173+
Value: 1,
174+
Unit: Unit.Count,
175+
}),
176+
],
177+
}),
178+
]),
179+
}),
180+
}),
181+
);
182+
});
183+
184+
it("emits metrics for updated config events", async () => {
185+
(
186+
mockDeps.supplierConfigRepo.upsertSupplierConfig as jest.Mock
187+
).mockResolvedValue("UPDATED");
188+
const data = createSupplierConfig();
189+
const record = createSqsRecord(data);
190+
const event = { Records: [record] } as unknown as SQSEvent;
191+
192+
await handler(event);
193+
194+
expect(mockDeps.logger.info).toHaveBeenCalledWith(
195+
expect.objectContaining({
196+
entity: "supplier",
197+
result: "UPDATED",
198+
ConfigEvents: 1,
199+
_aws: expect.objectContaining({
200+
CloudWatchMetrics: expect.arrayContaining([
201+
expect.objectContaining({
202+
Metrics: [
203+
expect.objectContaining({
204+
Name: "ConfigEvents",
205+
Value: 1,
206+
Unit: Unit.Count,
207+
}),
208+
],
209+
}),
210+
]),
211+
}),
212+
}),
213+
);
214+
});
153215
});

lambdas/supplier-config-ingress/src/handler/supplier-config-ingress-handler.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import type { SQSBatchResponse, SQSEvent, SQSRecord } from "aws-lambda";
22
import { z } from "zod/v4";
3+
import { Unit } from "aws-embedded-metrics";
34
import {
45
$SupplierConfigEntity,
56
SupplierConfigEntity,
7+
SupplierConfigRepository,
68
} from "@internal/datastore";
79
import {
810
$LetterVariant,
@@ -12,9 +14,10 @@ import {
1214
$SupplierPack,
1315
$VolumeGroup,
1416
} from "@nhsdigital/nhs-notify-event-schemas-supplier-config";
17+
import { MetricEntry, buildEMFObject } from "@internal/helpers";
1518
import { Deps } from "../config/deps";
1619

17-
const $EventEnvelope = z.looseObject({
20+
const $EventEnvelope = z.object({
1821
type: z.string(),
1922
data: z.looseObject({ id: z.string() }),
2023
});
@@ -32,6 +35,12 @@ const entitySchemas: Record<SupplierConfigEntity, z.ZodType<{ id: string }>> = {
3235
"supplier-pack": $SupplierPack as unknown as z.ZodType<{ id: string }>,
3336
};
3437

38+
type UpsertResult = Awaited<
39+
ReturnType<SupplierConfigRepository["upsertSupplierConfig"]>
40+
>;
41+
42+
type MetricKey = { entity: string; result: UpsertResult };
43+
3544
function extractEntityFromType(type: string) {
3645
const elements = type.split(".");
3746
return $SupplierConfigEntity.parse(
@@ -51,11 +60,30 @@ function parseSupplierConfigFromRecord(record: SQSRecord): {
5160
return { entity, config };
5261
}
5362

63+
function emitMetrics(
64+
logger: Deps["logger"],
65+
resultCounts: Map<MetricKey, number>,
66+
) {
67+
const namespace = "supplier-config-ingress";
68+
for (const [key, count] of resultCounts) {
69+
const { entity, result } = key;
70+
const dimensions: Record<string, string> = { entity, result };
71+
const metric: MetricEntry = {
72+
key: "ConfigEvents",
73+
value: count,
74+
unit: Unit.Count,
75+
};
76+
const emf = buildEMFObject(namespace, dimensions, metric);
77+
logger.info(emf);
78+
}
79+
}
80+
5481
export default function createSupplierConfigIngressHandler(deps: Deps) {
5582
const { logger, supplierConfigRepo } = deps;
5683

5784
return async (sqsEvent: SQSEvent): Promise<SQSBatchResponse> => {
5885
const batchItemFailures: { itemIdentifier: string }[] = [];
86+
const resultCounts = new Map<MetricKey, number>();
5987

6088
for (const record of sqsEvent.Records) {
6189
try {
@@ -75,6 +103,9 @@ export default function createSupplierConfigIngressHandler(deps: Deps) {
75103
config,
76104
);
77105

106+
const metricKey: MetricKey = { entity, result };
107+
resultCounts.set(metricKey, (resultCounts.get(metricKey) || 0) + 1);
108+
78109
logger.info(
79110
{ entity, pk: config.id, result },
80111
"Supplier config upserted",
@@ -88,6 +119,8 @@ export default function createSupplierConfigIngressHandler(deps: Deps) {
88119
}
89120
}
90121

122+
emitMetrics(logger, resultCounts);
123+
91124
return { batchItemFailures };
92125
};
93126
}

package-lock.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)