Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
8e845cf
change to create a PR
Vlasis-Perdikidis Jan 20, 2026
b51d290
add my first EMF metrics to upsert letter lambda
Vlasis-Perdikidis Jan 21, 2026
6764866
correct handler function and lint errors
Vlasis-Perdikidis Jan 21, 2026
9e96b04
add cloudwatch:PutMetricData permission
Vlasis-Perdikidis Jan 21, 2026
53db977
set custom namespace
Vlasis-Perdikidis Jan 21, 2026
2f1513d
remove cloudwatch client declaration
Vlasis-Perdikidis Jan 21, 2026
c1a9bc2
remove oddOrEven from dimensions
Vlasis-Perdikidis Jan 22, 2026
9f9d1cf
add oddOrEven from dimensions
Vlasis-Perdikidis Jan 22, 2026
b2f012b
remove operationType from messageProcessed
Vlasis-Perdikidis Jan 23, 2026
32b30a1
log environment variables
Vlasis-Perdikidis Jan 23, 2026
ba37764
log letterEvent and operation
Vlasis-Perdikidis Jan 26, 2026
0233eb8
fix linting errors
Vlasis-Perdikidis Jan 26, 2026
6198984
emit metrics per supplier and with correct count
Vlasis-Perdikidis Jan 26, 2026
e965c60
correct emitMetrics function
Vlasis-Perdikidis Jan 26, 2026
038d755
add metrics for rest of Lambda functions
Vlasis-Perdikidis Jan 29, 2026
f022bf9
fix failing unit tests
Vlasis-Perdikidis Jan 30, 2026
f413b15
add extra test to check that statuses map is populated
Vlasis-Perdikidis Jan 30, 2026
a185e5f
address PR comments
Vlasis-Perdikidis Feb 3, 2026
1d7367e
fix npm vulnerabilities
Vlasis-Perdikidis Feb 3, 2026
ceff734
fix typeckeck and lint errors
Vlasis-Perdikidis Feb 3, 2026
9da31ac
remove comment
Vlasis-Perdikidis Feb 3, 2026
e46fdb7
Merge branch 'main' into feature/CCM-13610-Expose-key-metrics-to-Clou…
masl2 Feb 5, 2026
495657e
regen lock after merge
masl2 Feb 5, 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
4 changes: 2 additions & 2 deletions internal/datastore/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"dependencies": {
"@aws-sdk/client-dynamodb": "^3.858.0",
"@aws-sdk/lib-dynamodb": "^3.858.0",
"@aws-sdk/client-dynamodb": "^3.981.0",
"@aws-sdk/lib-dynamodb": "^3.981.0",
"@internal/helpers": "*",
"pino": "^9.7.0",
"zod": "^4.1.11",
Expand Down
7 changes: 4 additions & 3 deletions lambdas/api-handler/package.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
{
"dependencies": {
"@aws-sdk/client-dynamodb": "^3.925.0",
"@aws-sdk/client-dynamodb": "^3.981.0",
"@aws-sdk/client-s3": "^3.925.0",
"@aws-sdk/client-sqs": "^3.925.0",
"@aws-sdk/lib-dynamodb": "^3.925.0",
"@aws-sdk/s3-request-presigner": "^3.925.0",
"@internal/datastore": "*",
"@internal/helpers": "*",
"aws-lambda": "^1.0.7",
"esbuild": "^0.25.11",
"aws-embedded-metrics": "^4.2.1",
"aws-lambda": "^1.0.6",
"esbuild": "0.27.2",
"pino": "^9.7.0",
"zod": "^4.1.11"
},
Expand Down
83 changes: 49 additions & 34 deletions lambdas/api-handler/src/handlers/get-letter-data.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,66 @@
import { APIGatewayProxyHandler } from "aws-lambda";
import { MetricsLogger, metricScope } from "aws-embedded-metrics";
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,
): APIGatewayProxyHandler {
return async (event) => {
const commonIds = extractCommonIds(
event.headers,
event.requestContext,
deps,
);

if (!commonIds.ok) {
return processError(
commonIds.error,
commonIds.correlationId,
deps.logger,
return metricScope((metrics: MetricsLogger) => {
return async (event) => {
const commonIds = extractCommonIds(
event.headers,
event.requestContext,
deps,
);
}

try {
const letterId = assertNotEmpty(
event.pathParameters?.id,
new ValidationError(
ApiErrorDetail.InvalidRequestMissingLetterIdPathParameter,
),
);
if (!commonIds.ok) {
return processError(
commonIds.error,
commonIds.correlationId,
deps.logger,
);
}

return {
statusCode: 303,
headers: {
Location: await getLetterDataUrl(
commonIds.value.supplierId,
letterId,
deps,
const { supplierId } = commonIds.value;
try {
const letterId = assertNotEmpty(
event.pathParameters?.id,
new ValidationError(
ApiErrorDetail.InvalidRequestMissingLetterIdPathParameter,
),
},
body: "",
};
} catch (error) {
return processError(error, commonIds.value.correlationId, deps.logger);
}
};
);

emitForSingleSupplier(
metrics,
"getLetterData",
supplierId,
1,
MetricStatus.Success,
);
return {
statusCode: 303,
headers: {
Location: await getLetterDataUrl(supplierId, letterId, deps),
},
body: "",
};
} catch (error) {
emitForSingleSupplier(
metrics,
"getLetterData",
supplierId,
1,
MetricStatus.Failure,
);
return processError(error, commonIds.value.correlationId, deps.logger);
}
};
});
}
98 changes: 59 additions & 39 deletions lambdas/api-handler/src/handlers/get-letter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { APIGatewayProxyHandler } from "aws-lambda";
import { MetricsLogger, metricScope } from "aws-embedded-metrics";
import { assertNotEmpty } from "../utils/validation";
import { extractCommonIds } from "../utils/common-ids";
import ValidationError from "../errors/validation-error";
Expand All @@ -7,53 +8,72 @@ 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(
deps: Deps,
): APIGatewayProxyHandler {
return async (event) => {
const commonIds = extractCommonIds(
event.headers,
event.requestContext,
deps,
);

if (!commonIds.ok) {
return processError(
commonIds.error,
commonIds.correlationId,
deps.logger,
return metricScope((metrics: MetricsLogger) => {
return async (event) => {
const commonIds = extractCommonIds(
event.headers,
event.requestContext,
deps,
);
}

try {
const letterId = assertNotEmpty(
event.pathParameters?.id,
new ValidationError(
ApiErrorDetail.InvalidRequestMissingLetterIdPathParameter,
),
);
if (!commonIds.ok) {
return processError(
commonIds.error,
commonIds.correlationId,
deps.logger,
);
}

const letter = await getLetterById(
commonIds.value.supplierId,
letterId,
deps.letterRepo,
);
const { supplierId } = commonIds.value;
try {
const letterId = assertNotEmpty(
event.pathParameters?.id,
new ValidationError(
ApiErrorDetail.InvalidRequestMissingLetterIdPathParameter,
),
);

const letter = await getLetterById(
supplierId,
letterId,
deps.letterRepo,
);

const response = mapToGetLetterResponse(letter);
const response = mapToGetLetterResponse(letter);

deps.logger.info({
description: "Letter successfully fetched by id",
supplierId: commonIds.value.supplierId,
letterId,
});
deps.logger.info({
description: "Letter successfully fetched by id",
supplierId,
letterId,
});

return {
statusCode: 200,
body: JSON.stringify(response, null, 2),
};
} catch (error) {
return processError(error, commonIds.value.correlationId, deps.logger);
}
};
emitForSingleSupplier(
metrics,
"getLetter",
supplierId,
1,
MetricStatus.Success,
);
return {
statusCode: 200,
body: JSON.stringify(response, null, 2),
};
} catch (error) {
emitForSingleSupplier(
metrics,
"getLetter",
supplierId,
1,
MetricStatus.Failure,
);
return processError(error, commonIds.value.correlationId, deps.logger);
}
};
});
}
104 changes: 62 additions & 42 deletions lambdas/api-handler/src/handlers/get-letters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
APIGatewayProxyHandler,
} from "aws-lambda";
import { Logger } from "pino";
import { MetricsLogger, metricScope } from "aws-embedded-metrics";
import { getLettersForSupplier } from "../services/letter-operations";
import { extractCommonIds } from "../utils/common-ids";
import { requireEnvVar } from "../utils/validation";
Expand All @@ -11,7 +12,9 @@ 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 Expand Up @@ -82,53 +85,70 @@ function getLimitOrDefault(
export default function createGetLettersHandler(
deps: Deps,
): APIGatewayProxyHandler {
return async (event) => {
const commonIds = extractCommonIds(
event.headers,
event.requestContext,
deps,
);

if (!commonIds.ok) {
return processError(
commonIds.error,
commonIds.correlationId,
deps.logger,
return metricScope((metrics: MetricsLogger) => {
return async (event) => {
const commonIds = extractCommonIds(
event.headers,
event.requestContext,
deps,
);
}

try {
const maxLimit = requireEnvVar(deps.env, "MAX_LIMIT");
if (!commonIds.ok) {
return processError(
commonIds.error,
commonIds.correlationId,
deps.logger,
);
}

const limitNumber = getLimitOrDefault(
event.queryStringParameters,
maxLimit,
deps.logger,
);
const { supplierId } = commonIds.value;
try {
const maxLimit = requireEnvVar(deps.env, "MAX_LIMIT");

const letters = await getLettersForSupplier(
commonIds.value.supplierId,
status,
limitNumber,
deps.letterRepo,
);
const limitNumber = getLimitOrDefault(
event.queryStringParameters,
maxLimit,
deps.logger,
);

const letters = await getLettersForSupplier(
supplierId,
status,
limitNumber,
deps.letterRepo,
);

const response = mapToGetLettersResponse(letters);
const response = mapToGetLettersResponse(letters);

deps.logger.info({
description: "Pending letters successfully fetched",
supplierId: commonIds.value.supplierId,
limitNumber,
status,
lettersCount: letters.length,
});
deps.logger.info({
description: "Pending letters successfully fetched",
supplierId,
limitNumber,
status,
lettersCount: letters.length,
});

return {
statusCode: 200,
body: JSON.stringify(response, null, 2),
};
} catch (error) {
return processError(error, commonIds.value.correlationId, deps.logger);
}
};
emitForSingleSupplier(
metrics,
"getLetters",
supplierId,
letters.length,
MetricStatus.Success,
);
return {
statusCode: 200,
body: JSON.stringify(response, null, 2),
};
} catch (error) {
emitForSingleSupplier(
metrics,
"getLetters",
supplierId,
1,
MetricStatus.Failure,
);
return processError(error, commonIds.value.correlationId, deps.logger);
}
};
});
}
Loading
Loading