Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
e84bf1c
log EMF object for letter-status-update failure
Vlasis-Perdikidis Feb 10, 2026
3a38fc3
Merge branch 'main' into fix/CCM-14508-metricsNotTracked
masl2 Feb 10, 2026
460aa00
log separate entries for batch post letters
Vlasis-Perdikidis Feb 11, 2026
f3f93c9
merge main
Vlasis-Perdikidis Feb 11, 2026
56b8727
fix lint error
Vlasis-Perdikidis Feb 11, 2026
a69a638
log process.env for lambda runtime environment
Vlasis-Perdikidis Feb 12, 2026
aa23c01
stringify the process.env object
Vlasis-Perdikidis Feb 12, 2026
8f3d6e3
correct metrics for the rest of the lambdas
Vlasis-Perdikidis Feb 12, 2026
fa49df9
add another dimension to postLetters success
Vlasis-Perdikidis Feb 16, 2026
b0611b4
correct dimensions creation
Vlasis-Perdikidis Feb 16, 2026
0a5c4f9
clarify post-mi metrics
Vlasis-Perdikidis Feb 17, 2026
e08cdb3
record the quantity of the lineItems
Vlasis-Perdikidis Feb 17, 2026
c996b08
correct dimensions in transformers
Vlasis-Perdikidis Feb 17, 2026
29295a2
add metric name for mi-updates-transformer
Vlasis-Perdikidis Feb 17, 2026
58305e5
Merge branch 'main' into fix/CCM-14508-metricsNotTracked
masl2 Feb 17, 2026
a50cc00
add metrics to amendment-event-transformer
Vlasis-Perdikidis Feb 17, 2026
386431c
Merge branch 'main' into fix/CCM-14508-metricsNotTracked
Vlasis-Perdikidis Feb 17, 2026
9fdf330
resolve npm vulnerabilities
Vlasis-Perdikidis Feb 18, 2026
34b4f74
move metrics to internal folder
Vlasis-Perdikidis Feb 23, 2026
c487d5d
use metrics from internal helpers folder
Vlasis-Perdikidis Feb 23, 2026
1fa3bc9
use metrics from internal helpers folder for amendment-event-transformer
Vlasis-Perdikidis Feb 24, 2026
231a354
revert minimatch to original version
Vlasis-Perdikidis Feb 24, 2026
0cd4fe2
merge main
Vlasis-Perdikidis Feb 24, 2026
e836a14
npm ci
Vlasis-Perdikidis Feb 24, 2026
c4d892e
resolve unit test and lint failures
Vlasis-Perdikidis Feb 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions internal/helpers/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"dependencies": {
"aws-embedded-metrics": "^4.2.1",
"pino": "^10.3.0",
"zod": "^4.1.11"
},
Expand Down
1 change: 1 addition & 0 deletions internal/helpers/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export * from "./version";
export { default as $Environment } from "./environment";
export * from "./id-ref";
export * from "./logger";
export * from "./metrics";
55 changes: 55 additions & 0 deletions internal/helpers/src/metrics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { MetricsLogger, Unit } from "aws-embedded-metrics";

export function emitForSingleSupplier(
metrics: MetricsLogger,
functionName: string,
supplierId: string,
count: number,
message: string,
dimensions?: Record<string, string>,
) {
metrics.setNamespace(process.env.AWS_LAMBDA_FUNCTION_NAME || functionName);
metrics.putDimensions({
...dimensions,
Supplier: supplierId,
});
metrics.putMetric(message, count, Unit.Count);
}

export enum MetricStatus {
Success = "success",
Failure = "failure",
}

export interface MetricEntry {
key: string;
value: number;
unit: Unit;
}

// build EMF object
export function buildEMFObject(
functionName: string,
dimensions: Record<string, string>,
metric: MetricEntry,
) {
const namespace = process.env.AWS_LAMBDA_FUNCTION_NAME || functionName;
return {
LogGroup: namespace,
ServiceName: namespace,
...dimensions,
_aws: {
Timestamp: Date.now(),
CloudWatchMetrics: [
{
Namespace: namespace,
Dimensions: [[...Object.keys(dimensions), "ServiceName", "LogGroup"]],
Metrics: [
{ Name: metric.key, Value: metric.value, Unit: metric.unit },
],
},
],
},
[metric.key]: metric.value,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { SQSBatchItemFailure, SQSEvent, SQSHandler } from "aws-lambda";
import { PublishCommand } from "@aws-sdk/client-sns";
import { LetterEvent } from "@nhsdigital/nhs-notify-event-schemas-supplier-api/src/events/letter-events";
import { mapLetterToCloudEvent } from "@nhsdigital/nhs-notify-event-schemas-supplier-api/src/events/letter-mapper";
import { Unit } from "aws-embedded-metrics";
import pino from "pino";
import { MetricEntry, MetricStatus, buildEMFObject } from "@internal/helpers";
import {
UpdateLetterCommand,
UpdateLetterCommandSchema,
Expand Down Expand Up @@ -39,6 +42,11 @@ export default function createTransformAmendmentEventHandler(
messageId: message.messageId,
correlationId: message.messageAttributes.CorrelationId.stringValue,
});
emitSuccessMetrics(
updateLetterCommand.supplierId,
updateLetterCommand.status,
deps.logger,
);
} catch (error) {
deps.logger.error({
description: "Error processing letter status update",
Expand All @@ -52,7 +60,7 @@ export default function createTransformAmendmentEventHandler(
});

await Promise.all(tasks);

emitFailedItems(batchItemFailures, deps.logger);
return { batchItemFailures };
};
}
Expand All @@ -66,3 +74,43 @@ function buildSnsCommand(
Message: JSON.stringify(letterEvent),
});
}

function emitSuccessMetrics(
supplierId: string,
status: string,
logger: pino.Logger,
) {
const dimensions: Record<string, string> = {
supplier: supplierId,
status,
};
const metric: MetricEntry = {
key: MetricStatus.Success,
value: 1,
unit: Unit.Count,
};
const emf = buildEMFObject("amendment-event-transformer", dimensions, metric);
logger.info(emf);
}

function emitFailedItems(
batchFailures: SQSBatchItemFailure[],
logger: pino.Logger,
) {
for (const item of batchFailures) {
const dimensions: Record<string, string> = {
identifier: item.itemIdentifier,
};
const metric: MetricEntry = {
key: MetricStatus.Failure,
value: 1,
unit: Unit.Count,
};
const emf = buildEMFObject(
"amendment-event-transformer",
dimensions,
metric,
);
logger.info(emf);
}
}
2 changes: 1 addition & 1 deletion lambdas/api-handler/src/handlers/get-letter-data.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { APIGatewayProxyHandler } from "aws-lambda";
import { MetricsLogger, metricScope } from "aws-embedded-metrics";
import { MetricStatus, emitForSingleSupplier } from "@internal/helpers";
import { assertNotEmpty } from "../utils/validation";
import { extractCommonIds } from "../utils/common-ids";
import { ApiErrorDetail } from "../contracts/errors";
import { processError } from "../mappers/error-mapper";
import ValidationError from "../errors/validation-error";
import { getLetterDataUrl } from "../services/letter-operations";
import type { Deps } from "../config/deps";
import { MetricStatus, emitForSingleSupplier } from "../utils/metrics";

export default function createGetLetterDataHandler(
deps: Deps,
Expand Down
2 changes: 1 addition & 1 deletion lambdas/api-handler/src/handlers/get-letter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { APIGatewayProxyHandler } from "aws-lambda";
import { MetricsLogger, metricScope } from "aws-embedded-metrics";
import { MetricStatus, emitForSingleSupplier } from "@internal/helpers";
import { assertNotEmpty } from "../utils/validation";
import { extractCommonIds } from "../utils/common-ids";
import ValidationError from "../errors/validation-error";
Expand All @@ -8,7 +9,6 @@ import { getLetterById } from "../services/letter-operations";
import { processError } from "../mappers/error-mapper";
import { mapToGetLetterResponse } from "../mappers/letter-mapper";
import { Deps } from "../config/deps";
import { MetricStatus, emitForSingleSupplier } from "../utils/metrics";

// Get letter data
export default function createGetLetterHandler(
Expand Down
3 changes: 1 addition & 2 deletions lambdas/api-handler/src/handlers/get-letters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
} from "aws-lambda";
import { Logger } from "pino";
import { MetricsLogger, metricScope } from "aws-embedded-metrics";
import { MetricStatus, emitForSingleSupplier } from "@internal/helpers";
import { getLettersForSupplier } from "../services/letter-operations";
import { extractCommonIds } from "../utils/common-ids";
import { requireEnvVar } from "../utils/validation";
Expand All @@ -12,9 +13,7 @@ import { processError } from "../mappers/error-mapper";
import ValidationError from "../errors/validation-error";
import { mapToGetLettersResponse } from "../mappers/letter-mapper";
import type { Deps } from "../config/deps";
import { MetricStatus, emitForSingleSupplier } from "../utils/metrics";

// List letters Handlers
// The endpoint should only return pending letters for now
const status = "PENDING";

Expand Down
16 changes: 11 additions & 5 deletions lambdas/api-handler/src/handlers/patch-letter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { APIGatewayProxyHandler } from "aws-lambda";
import { MetricsLogger, Unit, metricScope } from "aws-embedded-metrics";
import { MetricStatus } from "@internal/helpers";
import { enqueueLetterUpdateRequests } from "../services/letter-operations";
import {
PatchLetterRequest,
Expand All @@ -13,7 +14,6 @@ import { assertNotEmpty } from "../utils/validation";
import { extractCommonIds } from "../utils/common-ids";
import { mapToUpdateCommand } from "../mappers/letter-mapper";
import type { Deps } from "../config/deps";
import { MetricStatus } from "../utils/metrics";

export default function createPatchLetterHandler(
deps: Deps,
Expand Down Expand Up @@ -56,6 +56,7 @@ export default function createPatchLetterHandler(
try {
patchLetterRequest = PatchLetterRequestSchema.parse(JSON.parse(body));
} catch (error) {
emitErrorMetric(metrics, supplierId);
const typedError =
error instanceof Error
? new ValidationError(ApiErrorDetail.InvalidRequestBody, {
Expand All @@ -79,6 +80,7 @@ export default function createPatchLetterHandler(
);

if (updateLetterCommand.id !== letterId) {
emitErrorMetric(metrics, supplierId);
throw new ValidationError(
ApiErrorDetail.InvalidRequestLetterIdsMismatch,
);
Expand All @@ -100,12 +102,16 @@ export default function createPatchLetterHandler(
body: "",
};
} catch (error) {
metrics.putDimensions({
supplier: supplierId,
});
metrics.putMetric(MetricStatus.Success, 1, Unit.Count);
emitErrorMetric(metrics, supplierId);
return processError(error, commonIds.value.correlationId, deps.logger);
}
};
});
}

function emitErrorMetric(metrics: MetricsLogger, supplierId: string) {
metrics.putDimensions({
supplier: supplierId,
});
metrics.putMetric(MetricStatus.Failure, 1, Unit.Count);
}
Loading
Loading