Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/feature/CB2-13670' into feature/…
Browse files Browse the repository at this point in the history
…CB2-13861
  • Loading branch information
naathanbrown committed Sep 23, 2024
2 parents 6b76a62 + 5775950 commit 766cee3
Show file tree
Hide file tree
Showing 15 changed files with 613 additions and 274 deletions.
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
"test:integration:github": "jest int --runInBand --globalSetup='./scripts/spin-up-lambdas.ts' --globalTeardown='./scripts/destroy-lambdas.ts' --testTimeout=20000",
"test:integration": "jest int --testTimeout=20000 --runInBand",
"test:hotfix:vrm": "jest --testTimeout 20000 hotfix/cb2-10791",
"test:hotfix:plates": "jest --testTimeout 20000 hotfix/cb2-11175",
"package": "npm run build:prod",
"start:ci": "npm run dynamo:seed && sam local start-api --docker-network $(docker network ls | grep github_network | awk '{print $2}') --warm-containers EAGER",
"swagger:open": "docker run --name swagger -d -p 80:8080 -v $(pwd)/docs:/tmp -e SWAGGER_FILE=/tmp/spec.yml swaggerapi/swagger-editor",
Expand Down
86 changes: 86 additions & 0 deletions src/handler/batchPlateCreation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import {
TechRecordType as TechRecordTypeByVehicle,
} from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-vehicle-type';
import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-verb';
import 'dotenv/config';
import { v4 as uuidv4 } from 'uuid';
import { SQSEvent } from 'aws-lambda';
import { PlateReasonForIssue, Plates } from '../models/plate';
import { DocumentName, SQSRequestBody } from '../models/sqsPayload';
import { getBySystemNumberAndCreatedTimestamp, inPlaceRecordUpdate } from '../services/database';
import { addToSqs } from '../services/sqs';
import { StatusCode } from '../util/enum';
import { flattenArrays, formatTechRecord } from '../util/formatTechRecord';
import logger, { logError } from '../util/logger';
import { BatchPlateData } from '../models/batchPlate';

export const handler = async (event: SQSEvent): Promise<void> => {
const batchIssuerName = 'CVS Batch Plate Generation';
let numberOfRecordsUpdated = 0;
let numberOfSqsAdded = 0;

try {
const processPromises = event.Records.map(async ({ body }) => {
const data: BatchPlateData = JSON.parse(body) as BatchPlateData;
const { systemNumber, createdTimestamp } = data;

logger.info(`Processing record: sysNum ${systemNumber}, timestamp ${createdTimestamp}`);

const dbRecord = await getBySystemNumberAndCreatedTimestamp(systemNumber, createdTimestamp);

if (!dbRecord || !Object.keys(dbRecord).length) {
throw new Error(`Missing record: sysNum ${systemNumber}, timestamp ${createdTimestamp}`);
}

if (dbRecord.techRecord_statusCode !== StatusCode.CURRENT) {
throw new Error(`Non current record: statusCode ${dbRecord.techRecord_statusCode}`);
}
if (dbRecord.techRecord_vehicleType !== 'trl' && dbRecord.techRecord_vehicleType !== 'hgv') {
throw new Error(`Invalid vehicle type: ${dbRecord.techRecord_vehicleType}`);
}

const newPlate: Plates = {
plateSerialNumber: uuidv4(),
plateIssueDate: new Date().toISOString(),
plateReasonForIssue: PlateReasonForIssue.REPLACEMENT,
plateIssuer: batchIssuerName,
};

const formattedTechRecord = formatTechRecord<TechRecordTypeByVehicle<'hgv' | 'trl'>>(dbRecord);

if (formattedTechRecord.techRecord_plates?.some((plate) => plate.plateIssuer === batchIssuerName) ?? false) {
logger.info(`Plate already issued for: sysNum ${systemNumber}, timestamp ${createdTimestamp}`);
return;
}

if (formattedTechRecord.techRecord_plates) {
formattedTechRecord.techRecord_plates.push(newPlate);
} else {
formattedTechRecord.techRecord_plates = [newPlate];
}
const flattenedTechRecord = flattenArrays(formattedTechRecord) as TechRecordType<'get'>;
await inPlaceRecordUpdate(flattenedTechRecord);
numberOfRecordsUpdated++;

const plateSqsPayload: SQSRequestBody = {
techRecord: formattedTechRecord,
plate: newPlate,
documentName: DocumentName.MINISTRY,
recipientEmailAddress: '',
};
logger.debug('Sending to Doc Gen Queue', JSON.stringify(plateSqsPayload));
await addToSqs(plateSqsPayload, process.env.DOC_GEN_SQS_QUEUE ?? '');

numberOfSqsAdded++;

logger.info(`Successfully processed: sysNum ${systemNumber}, timestamp ${createdTimestamp}`);
});

await Promise.all(processPromises);

logger.info(`Batch Plate: Updated ${numberOfRecordsUpdated} tech records and added ${numberOfSqsAdded} to SQS`);
} catch (err: unknown) {
logError('Error in batch processing', err);
throw (err);
}
};
96 changes: 96 additions & 0 deletions src/handler/loadBatchPlate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import {
S3Client, GetObjectCommand, CopyObjectCommand, DeleteObjectCommand,
} from '@aws-sdk/client-s3';
import { SQSClient, SendMessageCommand } from '@aws-sdk/client-sqs';
import { S3Event } from 'aws-lambda';
import { BatchPlateData } from '../models/batchPlate';
import logger, { logError } from '../util/logger';

const s3Client = new S3Client({ region: process.env.DYNAMO_AWS_REGION });
const sqsClient = new SQSClient({ region: process.env.DYNAMO_AWS_REGION });

export const handler = async (event: S3Event): Promise<void> => {
logger.info('Update end point called');

try {
await Promise.all(event.Records.map(processRecord));
logger.info(`Successfully processed ${event.Records.length} files.`);
} catch (error) {
logError('Failed to process one or more files', error);
throw error;
}
};
async function processRecord(record: S3Event['Records'][0]): Promise<void> {
const bucket = record.s3.bucket.name;
const key = decodeURIComponent(record.s3.object.key.replace(/\+/g, ' '));

logger.info(`Processing file: ${key} from ${bucket}`);

try {
const data = await retrieveJSON(bucket, key);
await Promise.all(data.map((item) => sendToQueue(item)));
await moveProcessedFile(bucket, key);
logger.info(`Successfully processed and moved file: ${key}}`);
} catch (error) {
logError(`Error processing file ${key}`, error);
throw error;
}
}

/**
* This function will retrieve the json file from the provided s3 bucket
* Then, extract and validate the json file content
* @param bucket
* @param key
*/
async function retrieveJSON(bucket: string, key: string): Promise<BatchPlateData[]> {
const command = new GetObjectCommand({ Bucket: bucket, Key: key });
const response = await s3Client.send(command);
const bodyContents = await response.Body?.transformToString();

if (!bodyContents) {
throw new Error('Empty JSON file');
}

try {
return JSON.parse(bodyContents) as BatchPlateData[];
} catch (error) {
throw new Error(`Invalid JSON in file: ${error instanceof Error ? error.message : (error as string)}`);
}
}

/**
* This function will send the systemNumber and createdTimestamp to the doc-gen service.
* @param item
*/
async function sendToQueue(item: BatchPlateData): Promise<void> {
const command = new SendMessageCommand({
QueueUrl: process.env.SQS_QUEUE_URL,
MessageBody: JSON.stringify(item),
});

await sqsClient.send(command);
}

/**
* This function will copy the file that has been processed and move it to the processed folder
* Then, it will delete the original.
* @param bucket
* @param key
*/
async function moveProcessedFile(bucket: string, key: string): Promise<void> {
const newKey = `processed/${key}`;

const copyCommand = new CopyObjectCommand({
Bucket: bucket,
CopySource: `${bucket}/${key}`,
Key: newKey,
});
await s3Client.send(copyCommand);

const deleteCommand = new DeleteObjectCommand({
Bucket: bucket,
Key: key,
});
await s3Client.send(deleteCommand);
}
10 changes: 0 additions & 10 deletions src/hotfix/cb2-11175/README.md

This file was deleted.

103 changes: 0 additions & 103 deletions src/hotfix/cb2-11175/batchPlateCreation.ts

This file was deleted.

Loading

0 comments on commit 766cee3

Please sign in to comment.