Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"id": "client3-colour-admail",
"name": "Client 3 Colour Admail",
"packSpecificationIds": [
"notify-admail-colour-whitemail"
"notify-admail-whitemail"
],
"priority": 50,
"status": "INT",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
],
"priority": 99,
"status": "INT",
"supplierId": "supplier1",
"type": "STANDARD",
"volumeGroupId": "volumeGroup-test3"
"volumeGroupId": "volumeGroup-test1"
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,26 @@ data "aws_iam_policy_document" "upsert_letter_lambda" {

resources = [
aws_dynamodb_table.letters.arn,
aws_dynamodb_table.idempotency.arn,
"${aws_dynamodb_table.letters.arn}/index/supplierStatus-index"
]
}

statement {
sid = "AllowIdempotencyTableWrite"
effect = "Allow"

actions = [
"dynamodb:PutItem",
"dynamodb:GetItem",
"dynamodb:UpdateItem",
"dynamodb:DeleteItem"
]

resources = [
aws_dynamodb_table.idempotency.arn,
]
}

statement {
sid = "AllowSQSRead"
effect = "Allow"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,7 @@ describe("createSupplierAllocatorHandler", () => {
expect(allocationConfig.selectSupplierByFactor).toHaveBeenCalledWith(
expect.any(Array),
expect.any(Array),
"letter1",
mockedDeps,
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,37 @@ describe("eligibleSuppliers", () => {
"Supplier service error",
);
});
it("should filter allocations by letterVariantSupplierId if provided", async () => {
(
supplierConfigService.getSupplierAllocationsForVolumeGroup as jest.Mock
).mockResolvedValue(mockSupplierAllocations);
(supplierConfigService.getSupplierDetails as jest.Mock).mockResolvedValue(
mockSuppliers,
);

const letterVariantSupplierId = "supplier-1";
await eligibleSuppliers(mockVolumeGroup, mockDeps, letterVariantSupplierId);
expect(supplierConfigService.getSupplierDetails).toHaveBeenCalledWith(
["supplier-1"],
mockDeps,
);
});
it("should log a warning if no allocations found for specified letter variant supplier", async () => {
(
supplierConfigService.getSupplierAllocationsForVolumeGroup as jest.Mock
).mockResolvedValue([]);
(supplierConfigService.getSupplierDetails as jest.Mock).mockResolvedValue(
[],
);

const letterVariantSupplierId = "supplier-1";
await eligibleSuppliers(mockVolumeGroup, mockDeps, letterVariantSupplierId);
expect(mockDeps.logger.warn).toHaveBeenCalledWith({
description: "No allocations found for specified letter variant supplier",
volumeGroupId: mockVolumeGroup.id,
letterVariantSupplierId,
});
});
});

describe("preferredSupplierPack", () => {
Expand Down Expand Up @@ -779,6 +810,7 @@ describe("selectSupplierByFactor", () => {
let mockDeps: jest.Mocked<Deps>;
let mockSuppliers: Supplier[];
let mockSupplierAllocations: SupplierAllocation[];
const domainId = "test-domain-id";

beforeEach(() => {
jest.clearAllMocks();
Expand Down Expand Up @@ -844,6 +876,7 @@ describe("selectSupplierByFactor", () => {
const result = await selectSupplierByFactor(
mockSuppliers,
mockSupplierAllocations,
domainId,
mockDeps,
);

Expand Down Expand Up @@ -875,6 +908,7 @@ describe("selectSupplierByFactor", () => {
const result = await selectSupplierByFactor(
mockSuppliers,
allocationsWithMissingSupplier,
domainId,
mockDeps,
);

Expand Down Expand Up @@ -906,6 +940,7 @@ describe("selectSupplierByFactor", () => {
const result = await selectSupplierByFactor(
mockSuppliers,
allocationsWithZeroPercentage,
domainId,
mockDeps,
);

Expand Down Expand Up @@ -936,7 +971,12 @@ describe("selectSupplierByFactor", () => {
];

await expect(
selectSupplierByFactor(mockSuppliers, zeroAllocations, mockDeps),
selectSupplierByFactor(
mockSuppliers,
zeroAllocations,
domainId,
mockDeps,
),
).rejects.toThrow(
"No valid supplier allocations found for suppliers with valid pack",
);
Expand All @@ -956,6 +996,7 @@ describe("selectSupplierByFactor", () => {
const result = await selectSupplierByFactor(
mockSuppliers,
mockSupplierAllocations,
domainId,
mockDeps,
);

Expand All @@ -975,6 +1016,7 @@ describe("selectSupplierByFactor", () => {
const result = await selectSupplierByFactor(
singleSupplier,
singleAllocation,
domainId,
mockDeps,
);

Expand All @@ -995,6 +1037,7 @@ describe("selectSupplierByFactor", () => {
const result = await selectSupplierByFactor(
mockSuppliers,
mockSupplierAllocations,
domainId,
mockDeps,
);

Expand All @@ -1015,6 +1058,7 @@ describe("selectSupplierByFactor", () => {
const result = await selectSupplierByFactor(
mockSuppliers,
mockSupplierAllocations,
domainId,
mockDeps,
);

Expand All @@ -1028,7 +1072,12 @@ describe("selectSupplierByFactor", () => {
).mockRejectedValue(error);

await expect(
selectSupplierByFactor(mockSuppliers, mockSupplierAllocations, mockDeps),
selectSupplierByFactor(
mockSuppliers,
mockSupplierAllocations,
domainId,
mockDeps,
),
).rejects.toThrow("Factor calculation error");
});

Expand All @@ -1053,6 +1102,7 @@ describe("selectSupplierByFactor", () => {
const result = await selectSupplierByFactor(
mockSuppliers,
allocationsWithUnrelatedSupplier,
domainId,
mockDeps,
);

Expand Down Expand Up @@ -1093,6 +1143,7 @@ describe("selectSupplierByFactor", () => {
const result = await selectSupplierByFactor(
manySuppliers,
manyAllocations,
domainId,
mockDeps,
);

Expand All @@ -1105,7 +1156,12 @@ describe("selectSupplierByFactor", () => {
).mockResolvedValue([]);

await expect(
selectSupplierByFactor(mockSuppliers, mockSupplierAllocations, mockDeps),
selectSupplierByFactor(
mockSuppliers,
mockSupplierAllocations,
domainId,
mockDeps,
),
).rejects.toThrow("No supplier factors could be calculated for allocation");
});
});
9 changes: 7 additions & 2 deletions lambdas/supplier-allocator/src/handler/allocate-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import {
selectSupplierByFactor,
suppliersWithValidPack,
} from "./allocation-config";

import { Deps } from "../config/deps";
import { PreparedEventSchema, PreparedEvents, SupplierDetails } from "./types";

Expand Down Expand Up @@ -94,7 +93,7 @@ async function getSupplierFromConfig(
);

const { supplierAllocations, suppliers: allocatedSuppliers } =
await eligibleSuppliers(volumeGroup, deps);
await eligibleSuppliers(volumeGroup, deps, letterVariant.supplierId);

const preferredPack: PackSpecification = await preferredSupplierPack(
letterEvent,
Expand Down Expand Up @@ -125,17 +124,20 @@ async function getSupplierFromConfig(
? await selectSupplierByFactor(
suppliersForPackWithCapacity,
supplierAllocations,
letterEvent.data.domainId,
deps,
)
: undefined) ??
(await selectSupplierByFactor(
allSuppliersForPack,
supplierAllocations,
letterEvent.data.domainId,
deps,
));

deps.logger.info({
description: "Fetched supplier details for supplier allocations",
domainId: letterEvent.data.domainId,
variantId: letterEvent.data.letterVariantId,
volumeGroupId: volumeGroup.id,
supplierAllocationIds: supplierAllocations.map((a) => a.id),
Expand Down Expand Up @@ -391,6 +393,9 @@ export default function createSupplierAllocatorHandler(deps: Deps): SQSHandler {

({ priority, supplier } = supplierAllocationResult);
} catch (error) {
console.log(
`Error processing allocation of record ${record.messageId}: ${error}`,
);
deps.logger.error({
description: "Error processing allocation of record",
err: error,
Expand Down
29 changes: 29 additions & 0 deletions lambdas/supplier-allocator/src/handler/allocation-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { PreparedEvents } from "./types";
export async function eligibleSuppliers(
volumeGroup: VolumeGroup,
deps: Deps,
letterVariantSupplierId?: string,
): Promise<{
supplierAllocations: SupplierAllocation[];
suppliers: Supplier[];
Expand All @@ -31,6 +32,32 @@ export async function eligibleSuppliers(
volumeGroup.id,
deps,
);
if (letterVariantSupplierId) {
deps.logger.info({
description: "Filtering allocations for letter variant supplier",
volumeGroupId: volumeGroup.id,
letterVariantSupplierId,
});
const filteredAllocations = supplierAllocations.find(
(alloc) => alloc.supplier === letterVariantSupplierId,
);
if (!filteredAllocations) {
deps.logger.warn({
description:
"No allocations found for specified letter variant supplier",
volumeGroupId: volumeGroup.id,
letterVariantSupplierId,
});
}
return {
supplierAllocations: filteredAllocations ? [filteredAllocations] : [],
suppliers: await getSupplierDetails(
filteredAllocations ? [filteredAllocations.supplier] : [],
deps,
),
};
}

const allocationPercentageSum = supplierAllocations.reduce(
(sum, alloc) => sum + alloc.allocationPercentage,
0,
Expand Down Expand Up @@ -121,6 +148,7 @@ export async function filterSuppliersWithCapacity(
export async function selectSupplierByFactor(
suppliers: Supplier[],
supplierAllocations: SupplierAllocation[],
domainId: string,
deps: Deps,
): Promise<string> {
const supplierAllocationsForPack = supplierAllocations.filter((alloc) => {
Expand Down Expand Up @@ -148,6 +176,7 @@ export async function selectSupplierByFactor(

deps.logger.info({
description: "Calculated supplier factors for allocation",
domainId,
supplierFactors,
});
let selectedSupplierId = supplierFactors[0].supplierId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ export async function filterPacksForLetter(
if (violatedConstraints.length > 0) {
deps.logger.info({
description: `Pack specification filtered out based on pageCount constraints`,
dommainId: letterEvent.data.domainId,
packSpecId,
pageCount,
violatedConstraints,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { randomUUID } from "node:crypto";
import { expect, test } from "@playwright/test";
import {
FetchedSupplierLog,
getAllocationDetailsForDomainId,
getVariantsForAllocation,
} from "tests/helpers/allocation-helper";

import { createPreparedV1Event } from "tests/helpers/event-fixtures";
import { sendSnsEvent } from "tests/helpers/send-sns-event";

test.describe("Letter Variant Tests", () => {
test.setTimeout(180_000); // 3 minutes for long running polling

test("Verify that supplierId on letter variant takes precedence over volume group", async () => {
const letterVariant = getVariantsForAllocation(3);
const domainId = randomUUID();
const preparedEvent = createPreparedV1Event({
domainId,
letterVariantId: letterVariant,
});

const response = await sendSnsEvent(preparedEvent);

expect(response.MessageId).toBeTruthy();

const allocationDetails: FetchedSupplierLog =
await getAllocationDetailsForDomainId(domainId);

expect(allocationDetails.allocatedSuppliers).toHaveLength(1);

const allocatedSupplierId = allocationDetails.allocatedSuppliers?.[0]?.id;
expect(allocatedSupplierId).toBe("supplier1");
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ test.describe("Allocator Lambda Tests", () => {

const supplierAllocatorLog = await getAllocationLog(
"Pack specification filtered out based on pageCount constraints",
{ extraPatterns: [domainId] },
);
const filteredPackSpecId = supplierAllocatorLog.packSpecId;
logger.info(`Pack spec filtered out ${filteredPackSpecId}`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ test.describe("Allocator Weighting Tests", () => {
"Calculated supplier factors for allocation",
{
startTimeMs: testStartedAt,
extraPatterns: [domainId],
},
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ test.describe("Supplier Allocation Tests", () => {
logger.info(
`New total daily allocation for date ${allocationDate}: ${newTotalDailyAllocation}`,
);
expect(newTotalDailyAllocation).toBe(originalTotalDailyAllocation + 1);
expect(newTotalDailyAllocation).toBeGreaterThanOrEqual(
originalTotalDailyAllocation + 1,
);
});
});
Loading
Loading