Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
41 changes: 41 additions & 0 deletions scripts/update-failed-letters.ts
Comment thread
masl2 marked this conversation as resolved.
Outdated
Comment thread
masl2 marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/usr/bin/env tsx

// Script: update-failed-letters.ts
// Usage: tsx scripts/update-failed-letters.ts <supplierId> <status>
// Description: Looks up letters for a given supplierId and status, and updates letter statuses to FAILED where groupId is longer than 100 characters.

import { LetterRepository } from "@internal/datastore/src/letter-repository";
import { getDbContext } from "@internal/datastore/src/db-context";
import pino from "pino";

const logger = pino({ level: "info" });

async function main() {
const [supplierId, status] = process.argv.slice(2);
if (!supplierId || !status) {
logger.error("Usage: tsx scripts/update-failed-letters.ts <supplierId> <status>");
process.exit(1);
}

const db = await getDbContext();
const letterRepo = new LetterRepository(db, logger);

logger.info(`Looking up letters for supplierId=${supplierId}, status=${status}`);
const letters = await letterRepo.getLettersBySupplierAndStatus(supplierId, status);
Comment thread
masl2 marked this conversation as resolved.
Outdated

let updatedCount = 0;
for (const letter of letters) {
if (letter.groupId && letter.groupId.length > 100) {
logger.info(`Updating letter ${letter.id} (groupId length: ${letter.groupId.length}) to FAILED`);
await letterRepo.updateLetterStatus(letter.id, "FAILED");
Comment thread
masl2 marked this conversation as resolved.
Outdated
updatedCount++;
}
}

logger.info(`Updated ${updatedCount} letters to FAILED.`);
Comment thread
masl2 marked this conversation as resolved.
Outdated
}

main().catch((err) => {
logger.error({ err }, "Script failed");
process.exit(1);
});
16 changes: 16 additions & 0 deletions scripts/utilities/letter-status-fixer/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"dependencies": {
"@aws-sdk/client-dynamodb": "^3.984.0",
"@aws-sdk/lib-dynamodb": "^3.1008.0",
"pino": "^10.3.0",
"yargs": "^17.7.2"
},
"main": "src/cli/index.ts",
"name": "letter-status-fixer",
"private": true,
"scripts": {
"fix-status": "tsx src/cli/index.ts fix-status"
},
"type": "module",
"version": "0.1.0"
}
65 changes: 65 additions & 0 deletions scripts/utilities/letter-status-fixer/src/cli/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { hideBin } from "yargs/helpers";
import yargs from "yargs";
import { QueryCommand, QueryCommandOutput, UpdateCommand } from "@aws-sdk/lib-dynamodb";
import { createLetterDocClient } from "../infrastructure/letters-repo-factory";

async function updateFailedLetters(environment: string, supplierId: string, status: string) {
const { docClient, log, config } = createLetterDocClient(environment);
const compoundKey = `${supplierId}#${status}`;

let lastKey: Record<string, any> | undefined = undefined;
let updatedCount = 0;

do {
Comment thread
masl2 marked this conversation as resolved.
const queryCmd = new QueryCommand({
TableName: config.lettersTableName,
IndexName: config.supplierStatusIndex,
KeyConditionExpression: "supplierStatus = :ss",
ExpressionAttributeValues: { ":ss": compoundKey },
ExclusiveStartKey: lastKey,
});
const result = await docClient.send(queryCmd) as QueryCommandOutput;
Comment thread
masl2 marked this conversation as resolved.
Outdated
for (const item of result.Items || []) {
if (item.groupId && item.groupId.length > 100) {
Comment thread
masl2 marked this conversation as resolved.
Outdated
log.info(`Updating letter ${item.letterId} (groupId length: ${item.groupId.length}) to FAILED`);
const updateCmd = new UpdateCommand({
TableName: config.lettersTableName,
Key: { letterId: item.letterId },
UpdateExpression: "SET #status = :failed",
ExpressionAttributeNames: { "#status": "status" },
ExpressionAttributeValues: { ":failed": "FAILED" },
});
await docClient.send(updateCmd);
Comment thread
masl2 marked this conversation as resolved.
Outdated
updatedCount++;
}
}
lastKey = result.LastEvaluatedKey;
} while (lastKey);

log.info(`Updated ${updatedCount} letters to FAILED.`);
Comment thread
masl2 marked this conversation as resolved.
Outdated
}

async function main() {
await yargs(hideBin(process.argv))
.command(
"fix-status",
"Update letters with long groupId to FAILED",
{
environment: { type: "string", demandOption: true },
supplierId: { type: "string", demandOption: true },
status: { type: "string", demandOption: true },
},
async (argv) => {
await updateFailedLetters(argv.environment, argv.supplierId, argv.status);
}
)
.demandCommand(1)
.parse();
}

if (require.main === module) {
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
import pino from "pino";

export function createLetterDocClient(environment: string) {
const ddbClient = new DynamoDBClient({});
const docClient = DynamoDBDocumentClient.from(ddbClient);
const log = pino();
const config = {
lettersTableName: process.env.LETTERS_TABLE || `nhs-${environment}-supapi-letters`,
supplierStatusIndex: process.env.SUPPLIER_STATUS_INDEX || "supplierStatus",
};
return { docClient, log, config };
}
17 changes: 17 additions & 0 deletions scripts/utilities/letter-status-fixer/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"esModuleInterop": true,
"module": "ES2020",
"moduleResolution": "node",
"outDir": "dist",
"resolveJsonModule": true,
"rootDir": "src",
"skipLibCheck": true,
"strict": true,
"target": "ES2022"
},
"extends": "../../../tsconfig.base.json",
"include": [
"src/**/*"
]
}
Loading