Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cb2-12590): refactor to use async await pattern when sending emails #119

Merged
merged 11 commits into from
Jul 3, 2024
26 changes: 13 additions & 13 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions src/functions/reportGen.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { LambdaClient } from "@aws-sdk/client-lambda";
import { PutObjectRequest } from "@aws-sdk/client-s3";
import { unmarshall } from '@aws-sdk/util-dynamodb';
import { unmarshall } from "@aws-sdk/util-dynamodb";
import { ServiceException } from "@smithy/smithy-client";
import { Callback, Context, Handler } from "aws-lambda";
import { ERRORS } from "../assets/enum";
Expand All @@ -27,14 +27,18 @@ const reportGen: Handler = async (event: any, context?: Context, callback?: Call

const sendATFReport: SendATFReport = new SendATFReport();

console.debug("Services injected, looping over visits");
event.Records.forEach((record: any) => {
const recordBody = JSON.parse(record?.body)
const recordBody = JSON.parse(record?.body);
const visit: any = unmarshall(recordBody?.dynamodb.NewImage);

console.debug(`visit is: ${JSON.stringify(visit)}`);

if (visit) {
const atfReportPromise = reportService
.generateATFReport(visit)
.then((generationServiceResponse) => {
console.debug("Inside generateATFReport promise, now creating send promises");
return sendATFReport.sendATFReport(generationServiceResponse, visit);
})
.catch((error: any) => {
Expand All @@ -46,6 +50,7 @@ const reportGen: Handler = async (event: any, context?: Context, callback?: Call
}
});

console.debug("About to send reports with promise all at bottom of handler.");
return Promise.all(atfReportPromises).catch((error: ServiceException) => {
console.error(error);
throw error;
Expand Down
11 changes: 6 additions & 5 deletions src/services/ActivitiesService.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { IInvokeConfig } from "../models";
import { InvocationRequest, InvocationResponse } from "@aws-sdk/client-lambda";
import { LambdaService } from "./LambdaService";
import { Configuration } from "../utils/Configuration";
import moment from "moment";
import { toUint8Array } from "@smithy/util-utf8";
import moment from "moment";
import { IInvokeConfig } from "../models";
import { Configuration } from "../utils/Configuration";
import { LambdaService } from "./LambdaService";

class ActivitiesService {
private readonly lambdaClient: LambdaService;
Expand All @@ -19,6 +19,7 @@ class ActivitiesService {
* @param params - getActivities query parameters
*/
public getActivities(params: any): Promise<any> {
console.log(`getActivities called with params: ${JSON.stringify(params)}`);
const config: IInvokeConfig = this.config.getInvokeConfig();
const invokeParams: InvocationRequest = {
FunctionName: config.functions.getActivities.name,
Expand All @@ -33,8 +34,8 @@ class ActivitiesService {
),
};

// TODO fail fast if activityType is not 'visit' as per CVSB-19853 - this code will be removed as part of the 'wait time epic'
if (params.activityType !== "visit") {
console.log("not a visit, resolving a promise with an empty array");
return Promise.resolve([]);
}

Expand Down
9 changes: 6 additions & 3 deletions src/services/NotificationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { ServiceException } from "@smithy/smithy-client";
import { HTTPError } from "../models/HTTPError";
// @ts-ignore
import { NotifyClient } from "notifications-node-client";
import { Configuration } from "../utils/Configuration";
import { EMAIL_TYPE } from "../assets/enum";
import { Configuration } from "../utils/Configuration";

/**
* Service class for Certificate Notifications
Expand All @@ -28,6 +28,7 @@ class NotificationService {
const emailDetails = {
personalisation: params,
};
// @ts-ignore
naathanbrown marked this conversation as resolved.
Show resolved Hide resolved
const sendEmailPromise = [];

for (const email of emails) {
Expand All @@ -36,12 +37,14 @@ class NotificationService {
}

if (emailType === EMAIL_TYPE.ATF) {
naathanbrown marked this conversation as resolved.
Show resolved Hide resolved
console.log(`report successfully sent to ATF for PNumber ${params.testStationPNumber} with activity ${activityId}.`);
console.log(`report successfully created promise for ATF for PNumber ${params.testStationPNumber} with activity ${activityId}.`);
} else if (emailType === EMAIL_TYPE.VSA) {
console.log(`report successfully sent to VSA for PNumber ${params.testStationPNumber} with activity ${activityId}.`);
console.log(`report successfully created promise for VSA for PNumber ${params.testStationPNumber} with activity ${activityId}.`);
}

return Promise.all(sendEmailPromise).catch((error: ServiceException) => {
// @ts-ignore
console.log(`failed to send for ${JSON.stringify(sendEmailPromise)}`);
naathanbrown marked this conversation as resolved.
Show resolved Hide resolved
console.error(error);
throw new HTTPError(error.$response?.statusCode, error.message);
});
Expand Down
5 changes: 3 additions & 2 deletions src/services/ReportGenerationService.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { IActivity } from "../models";
import { TestResultsService } from "./TestResultsService";
import { ERRORS, STATUSES } from "../assets/enum";
import { IActivity } from "../models";
import { HTTPError } from "../models/HTTPError";
import { ActivitiesService } from "./ActivitiesService";
import { TestResultsService } from "./TestResultsService";

class ReportGenerationService {
private readonly testResultsService: TestResultsService;
Expand All @@ -18,6 +18,7 @@ class ReportGenerationService {
* @param activity - activity for which to generate the report
*/
public generateATFReport(activity: IActivity): Promise<any> {
console.debug("Inside generateATFReport");
naathanbrown marked this conversation as resolved.
Show resolved Hide resolved
return this.testResultsService
.getTestResults({
testerStaffId: activity.testerStaffId,
Expand Down
8 changes: 5 additions & 3 deletions src/services/SendATFReport.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { LambdaClient } from "@aws-sdk/client-lambda";
// @ts-ignore
import { NotifyClient } from "notifications-node-client";
import { LambdaClient } from "@aws-sdk/client-lambda";
import { ACTIVITY_TYPE, EMAIL_TYPE } from "../assets/enum";
import { Configuration } from "../utils/Configuration";
import { IActivitiesList, IActivity, ITestResults } from "../models";
import { LambdaService } from "./LambdaService";
import { Configuration } from "../utils/Configuration";
import { NotificationData } from "../utils/generateNotificationData";
import { LambdaService } from "./LambdaService";
import { NotificationService } from "./NotificationService";
import { TestStationsService } from "./TestStationsService";

Expand All @@ -30,7 +30,9 @@ class SendATFReport {
const activitiesList = this.computeActivitiesList(generationServiceResponse.testResults, generationServiceResponse.waitActivities);

const response = await this.testStationsService.getTestStationEmail(visit.testStationPNumber);
console.debug(`response from get test station email: ${JSON.stringify(response)}`);
naathanbrown marked this conversation as resolved.
Show resolved Hide resolved
const sendNotificationData = this.notificationData.generateActivityDetails(visit, activitiesList);
console.debug(`send notification data : ${JSON.stringify(sendNotificationData)}`);
naathanbrown marked this conversation as resolved.
Show resolved Hide resolved
if (!this.notifyService) {
if (!this.apiKey) {
this.apiKey = (await Configuration.getInstance().getGovNotifyConfig()).api_key;
Expand Down
11 changes: 7 additions & 4 deletions src/services/TestResultsService.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { IInvokeConfig } from "../models";
import { InvocationRequest, InvocationResponse } from "@aws-sdk/client-lambda";
import { LambdaService } from "./LambdaService";
import { Configuration } from "../utils/Configuration";
import moment from "moment";
import { toUint8Array } from "@smithy/util-utf8";
import moment from "moment";
import { IInvokeConfig } from "../models";
import { Configuration } from "../utils/Configuration";
import { LambdaService } from "./LambdaService";

class TestResultsService {
private readonly lambdaClient: LambdaService;
Expand All @@ -19,6 +19,7 @@ class TestResultsService {
* @param params - getTestResultsByTesterStaffId query parameters
*/
public getTestResults(params: any): Promise<any> {
console.debug(`inside get test results: ${JSON.stringify(params)}`);
const config: IInvokeConfig = this.config.getInvokeConfig();
const invokeParams: InvocationRequest = {
FunctionName: config.functions.testResults.name,
Expand All @@ -37,6 +38,7 @@ class TestResultsService {
const payload: any = this.lambdaClient.validateInvocationResponse(response); // Response validation
const testResults: any[] = JSON.parse(payload.body); // Response conversion

console.debug(`test result response is: ${JSON.stringify(testResults)}`);
// Sort results by testTypeEndTimeStamp
testResults.sort((first: any, second: any): number => {
if (moment(first.testTypes[0].testTypeEndTimeStamp).isBefore(second.testTypes[0].testTypeEndTimeStamp)) {
Expand All @@ -60,6 +62,7 @@ class TestResultsService {
* @param testResults
*/
public expandTestResults(testResults: any): any[] {
console.debug("Splitting test results into multiple records");
return testResults
.map((testResult: any) => {
// Separate each test type in a record to form multiple test results
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/reportGenFunction.unitTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { SendATFReport } from "../../src/services/SendATFReport";
jest.mock("../../src/services/ReportGenerationService");
jest.mock("../../src/services/SendATFReport");

const mockPayload = '{"eventID":"f9e63bf29bd6adf174e308201a97259f","eventName":"MODIFY","eventVersion":"1.1","eventSource":"aws:dynamodb","awsRegion":"eu-west-1","dynamodb":{"ApproximateCreationDateTime":1711549645,"Keys":{"id":{"S":"6e4bd304-446e-4678-8289-dasdasjkl"}},"NewImage":{"testerStaffId":{"S":"132"},"testStationPNumber":{"S":"87-1369564"},"testerEmail":{"S":"tester@dvsa.gov.uk1111"},"testStationType":{"S":"gvts"},"testStationEmail":{"S":"teststationname@dvsa.gov.uk"},"startTime":{"S":"2022-01-01T10:00:40.561Z"},"endTime":{"S":"2022-01-01T10:00:40.561Z"},"id":{"S":"6e4bd304-446e-4678-8289-dasdasjkl"},"testStationName":{"S":"Rowe, Wunsch and Wisoky"},"activityType":{"S":"visit"},"activityDay":{"S":"2022-01-01"},"testerName":{"S":"namey mcname"}},"OldImage":{"testerStaffId":{"S":"132"},"testStationPNumber":{"S":"87-1369564"},"testerEmail":{"S":"tester@dvsa.gov.uk1111"},"testStationType":{"S":"gvts"},"testStationEmail":{"S":"teststationname@dvsa.gov.uk"},"startTime":{"S":"2022-01-01T10:00:40.561Z"},"endTime":{"S":"2022-01-01T10:00:40.561Z"},"id":{"S":"6e4bd304-446e-4678-8289-dasdasjkl"},"testStationName":{"S":"Rowe, Wunsch and Wisoky"},"activityType":{"S":"visit"},"activityDay":{"S":"2022-01-01"},"testerName":{"S":"231232132"}},"SequenceNumber":"1234","SizeBytes":704,"StreamViewType":"NEW_AND_OLD_IMAGES"},"eventSourceARN":"arn:aws::eu--1::/cvs---//:32:37.491"}'
const mockPayload = '{"eventID":"f9e63bf29bd6adf174e308201a97259f","eventName":"MODIFY","eventVersion":"1.1","eventSource":"aws:dynamodb","awsRegion":"eu-west-1","dynamodb":{"ApproximateCreationDateTime":1711549645,"Keys":{"id":{"S":"6e4bd304-446e-4678-8289-dasdasjkl"}},"NewImage":{"testerStaffId":{"S":"132"},"testStationPNumber":{"S":"87-1369564"},"testerEmail":{"S":"tester@dvsa.gov.uk1111"},"testStationType":{"S":"gvts"},"testStationEmail":{"S":"teststationname@dvsa.gov.uk"},"startTime":{"S":"2022-01-01T10:00:40.561Z"},"endTime":{"S":"2022-01-01T10:00:40.561Z"},"id":{"S":"6e4bd304-446e-4678-8289-dasdasjkl"},"testStationName":{"S":"Rowe, Wunsch and Wisoky"},"activityType":{"S":"visit"},"activityDay":{"S":"2022-01-01"},"testerName":{"S":"namey mcname"}},"OldImage":{"testerStaffId":{"S":"132"},"testStationPNumber":{"S":"87-1369564"},"testerEmail":{"S":"tester@dvsa.gov.uk1111"},"testStationType":{"S":"gvts"},"testStationEmail":{"S":"teststationname@dvsa.gov.uk"},"startTime":{"S":"2022-01-01T10:00:40.561Z"},"endTime":{"S":"2022-01-01T10:00:40.561Z"},"id":{"S":"6e4bd304-446e-4678-8289-dasdasjkl"},"testStationName":{"S":"Rowe, Wunsch and Wisoky"},"activityType":{"S":"visit"},"activityDay":{"S":"2022-01-01"},"testerName":{"S":"231232132"}},"SequenceNumber":"1234","SizeBytes":704,"StreamViewType":"NEW_AND_OLD_IMAGES"},"eventSourceARN":"arn:aws::eu--1::/cvs---//:32:37.491"}';

describe("Retro Gen Function", () => {
beforeAll(() => jest.setTimeout(60000));
Expand Down
Loading