Skip to content

Commit 9fe01d9

Browse files
authored
chore: move SQS handlers to their own folders (#159)
1 parent b067442 commit 9fe01d9

File tree

12 files changed

+173
-147
lines changed

12 files changed

+173
-147
lines changed

src/api/sqs/handlers.ts

Lines changed: 0 additions & 128 deletions
This file was deleted.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { AvailableSQSFunctions } from "common/types/sqsMessage.js";
2+
import {
3+
currentEnvironmentConfig,
4+
runEnvironment,
5+
SQSHandlerFunction,
6+
} from "../index.js";
7+
import { environmentConfig, genericConfig } from "common/config.js";
8+
import { getAuthorizedClients } from "../utils.js";
9+
import { getEntraIdToken, getUserProfile } from "api/functions/entraId.js";
10+
import { issueAppleWalletMembershipCard } from "api/functions/mobileWallet.js";
11+
import { generateMembershipEmailCommand } from "api/functions/ses.js";
12+
import { SESClient } from "@aws-sdk/client-ses";
13+
14+
export const emailMembershipPassHandler: SQSHandlerFunction<
15+
AvailableSQSFunctions.EmailMembershipPass
16+
> = async (payload, metadata, logger) => {
17+
const email = payload.email;
18+
const commonConfig = { region: genericConfig.AwsRegion };
19+
const clients = await getAuthorizedClients(logger, commonConfig);
20+
const entraIdToken = await getEntraIdToken(
21+
clients,
22+
currentEnvironmentConfig.AadValidClientId,
23+
);
24+
const userProfile = await getUserProfile(entraIdToken, email);
25+
const pkpass = await issueAppleWalletMembershipCard(
26+
clients,
27+
environmentConfig[runEnvironment],
28+
runEnvironment,
29+
email,
30+
metadata.initiator,
31+
logger,
32+
userProfile.displayName,
33+
);
34+
const emailCommand = generateMembershipEmailCommand(
35+
email,
36+
`membership@${environmentConfig[runEnvironment].EmailDomain}`,
37+
pkpass as any,
38+
);
39+
if (runEnvironment === "dev" && email === "[email protected]") {
40+
return;
41+
}
42+
const sesClient = new SESClient(commonConfig);
43+
return await sesClient.send(emailCommand);
44+
};

src/api/sqs/emailNotifications.ts renamed to src/api/sqs/handlers/emailNotifications.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { AvailableSQSFunctions } from "common/types/sqsMessage.js";
2-
import { currentEnvironmentConfig, SQSHandlerFunction } from "./index.js";
2+
import { currentEnvironmentConfig, SQSHandlerFunction } from "../index.js";
33
import { SendEmailCommand, SESClient } from "@aws-sdk/client-ses";
44
import { genericConfig } from "common/config.js";
55
import { createAuditLogEntry } from "api/functions/auditLog.js";

src/api/sqs/handlers/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export { pingHandler } from "./ping.js";
2+
export { emailMembershipPassHandler } from "./emailMembershipPassHandler.js";
3+
export { provisionNewMemberHandler } from "./provisionNewMember.js";
4+
export { sendSaleEmailHandler } from "./sendSaleEmailHandler.js";
5+
export { emailNotificationsHandler } from "./emailNotifications.js";

src/api/sqs/handlers/ping.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { AvailableSQSFunctions } from "common/types/sqsMessage.js";
2+
import { SQSHandlerFunction } from "../index.js";
3+
4+
export const pingHandler: SQSHandlerFunction<
5+
AvailableSQSFunctions.Ping
6+
> = async (_payload, _metadata, logger) => {
7+
logger.info("Pong!");
8+
};
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { AvailableSQSFunctions } from "common/types/sqsMessage.js";
2+
import { currentEnvironmentConfig, SQSHandlerFunction } from "../index.js";
3+
import {
4+
getEntraIdToken,
5+
getUserProfile,
6+
} from "../../../api/functions/entraId.js";
7+
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
8+
import { genericConfig } from "../../../common/config.js";
9+
10+
import { setPaidMembership } from "api/functions/membership.js";
11+
import { createAuditLogEntry } from "api/functions/auditLog.js";
12+
import { Modules } from "common/modules.js";
13+
import { getAuthorizedClients } from "../utils.js";
14+
import { emailMembershipPassHandler } from "./emailMembershipPassHandler.js";
15+
16+
export const provisionNewMemberHandler: SQSHandlerFunction<
17+
AvailableSQSFunctions.ProvisionNewMember
18+
> = async (payload, metadata, logger) => {
19+
const { email } = payload;
20+
const commonConfig = { region: genericConfig.AwsRegion };
21+
const clients = await getAuthorizedClients(logger, commonConfig);
22+
const entraToken = await getEntraIdToken(
23+
clients,
24+
currentEnvironmentConfig.AadValidClientId,
25+
);
26+
logger.info("Got authorized clients and Entra ID token.");
27+
const { updated } = await setPaidMembership({
28+
netId: email.replace("@illinois.edu", ""),
29+
dynamoClient: clients.dynamoClient,
30+
entraToken,
31+
paidMemberGroup: currentEnvironmentConfig.PaidMemberGroupId,
32+
});
33+
if (updated) {
34+
const logPromise = createAuditLogEntry({
35+
entry: {
36+
module: Modules.PROVISION_NEW_MEMBER,
37+
actor: metadata.initiator,
38+
target: email,
39+
message: "Marked target as a paid member.",
40+
},
41+
});
42+
logger.info(
43+
`${email} added as a paid member. Emailing their membership pass.`,
44+
);
45+
await emailMembershipPassHandler(payload, metadata, logger);
46+
await logPromise;
47+
} else {
48+
logger.info(`${email} was already a paid member.`);
49+
}
50+
};

src/api/sqs/sales.ts renamed to src/api/sqs/handlers/sendSaleEmailHandler.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { AvailableSQSFunctions } from "common/types/sqsMessage.js";
2-
import { currentEnvironmentConfig, SQSHandlerFunction } from "./index.js";
2+
import { currentEnvironmentConfig, SQSHandlerFunction } from "../index.js";
33
import { SESClient } from "@aws-sdk/client-ses";
44
import QRCode from "qrcode";
55
import { generateSalesEmail } from "api/functions/ses.js";
66
import { genericConfig } from "common/config.js";
77

8-
export const sendSaleEmailhandler: SQSHandlerFunction<
8+
export const sendSaleEmailHandler: SQSHandlerFunction<
99
AvailableSQSFunctions.SendSaleEmail
1010
> = async (payload, _metadata, logger) => {
1111
const { qrCodeContent } = payload;
@@ -15,7 +15,7 @@ export const sendSaleEmailhandler: SQSHandlerFunction<
1515
errorCorrectionLevel: "H",
1616
});
1717
logger.info("Constructing email...");
18-
const emailCommand = generateSalesEmail(payload, senderEmail, qrCode);
18+
const emailCommand = generateSalesEmail(payload, senderEmail, qrCode as any);
1919
logger.info("Constructing email...");
2020
const sesClient = new SESClient({ region: genericConfig.AwsRegion });
2121
const response = await sesClient.send(emailCommand);

src/api/sqs/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ import {
1616
emailMembershipPassHandler,
1717
pingHandler,
1818
provisionNewMemberHandler,
19-
} from "./handlers.js";
19+
sendSaleEmailHandler,
20+
emailNotificationsHandler,
21+
} from "./handlers/index.js";
2022
import { ValidationError } from "../../common/errors/index.js";
2123
import { RunEnvironment } from "../../common/roles.js";
2224
import { environmentConfig } from "../../common/config.js";
23-
import { sendSaleEmailhandler } from "./sales.js";
24-
import { emailNotificationsHandler } from "./emailNotifications.js";
2525

2626
export type SQSFunctionPayloadTypes = {
2727
[K in keyof typeof sqsPayloadSchemas]: SQSHandlerFunction<K>;
@@ -37,7 +37,7 @@ const handlers: SQSFunctionPayloadTypes = {
3737
[AvailableSQSFunctions.EmailMembershipPass]: emailMembershipPassHandler,
3838
[AvailableSQSFunctions.Ping]: pingHandler,
3939
[AvailableSQSFunctions.ProvisionNewMember]: provisionNewMemberHandler,
40-
[AvailableSQSFunctions.SendSaleEmail]: sendSaleEmailhandler,
40+
[AvailableSQSFunctions.SendSaleEmail]: sendSaleEmailHandler,
4141
[AvailableSQSFunctions.EmailNotifications]: emailNotificationsHandler,
4242
};
4343
export const runEnvironment = process.env.RunEnvironment as RunEnvironment;

src/api/sqs/utils.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
2+
import { SecretsManagerClient } from "@aws-sdk/client-secrets-manager";
3+
import { getRoleCredentials } from "api/functions/sts.js";
4+
import { genericConfig, roleArns } from "common/config.js";
5+
import pino from "pino";
6+
7+
export const getAuthorizedClients = async (
8+
logger: pino.Logger,
9+
commonConfig: { region: string },
10+
) => {
11+
if (roleArns.Entra) {
12+
logger.info(
13+
`Attempting to assume Entra role ${roleArns.Entra} to get the Entra token...`,
14+
);
15+
const credentials = await getRoleCredentials(roleArns.Entra);
16+
const clients = {
17+
smClient: new SecretsManagerClient({
18+
region: genericConfig.AwsRegion,
19+
credentials,
20+
}),
21+
dynamoClient: new DynamoDBClient({
22+
region: genericConfig.AwsRegion,
23+
credentials,
24+
}),
25+
};
26+
logger.info(`Assumed Entra role ${roleArns.Entra} to get the Entra token.`);
27+
return clients;
28+
}
29+
logger.debug("Did not assume Entra role as no env variable was present");
30+
return {
31+
smClient: new SecretsManagerClient(commonConfig),
32+
dynamoClient: new DynamoDBClient(commonConfig),
33+
};
34+
};

tests/tsconfig.json

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,9 @@
77
"skipLibCheck": true,
88
"forceConsistentCasingInFileNames": true,
99
"outDir": "../dist/tests",
10-
"rootDir": ".",
11-
"resolveJsonModule": true,
10+
"rootDir": "../",
11+
"resolveJsonModule": true
1212
},
13-
"include": [
14-
"./e2e/**/*.ts",
15-
"./live/**/*.ts",
16-
"./unit/**/*.ts"
17-
],
18-
"exclude": [
19-
"../node_modules",
20-
"../dist"
21-
]
13+
"include": ["../src/", "./e2e/**/*.ts", "./live/**/*.ts", "./unit/**/*.ts"],
14+
"exclude": ["../node_modules", "../dist"]
2215
}

tests/unit/sqs/handlers/ping.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { expect, test, vi } from "vitest";
2+
import { pingHandler } from "../../../../src/api/sqs/handlers/ping.js";
3+
4+
test("SQS Ping Handler test", () => {
5+
const pinoMock = {
6+
info: vi.fn(),
7+
}
8+
pingHandler({}, { reqId: "0", initiator: "1" }, pinoMock as any)
9+
expect(pinoMock.info).toHaveBeenCalledExactlyOnceWith("Pong!");
10+
})

yarn.lock

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5817,6 +5817,11 @@ flatted@^3.3.3:
58175817
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358"
58185818
integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==
58195819

5820+
follow-redirects@^1.15.6:
5821+
version "1.15.9"
5822+
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1"
5823+
integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==
5824+
58205825
for-each@^0.3.3:
58215826
version "0.3.4"
58225827
resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.4.tgz#814517ffc303d1399b2564d8165318e735d0341c"
@@ -8222,6 +8227,11 @@ prop-types@^15.6.2, prop-types@^15.8.1:
82228227
object-assign "^4.1.1"
82238228
react-is "^16.13.1"
82248229

8230+
proxy-from-env@^1.1.0:
8231+
version "1.1.0"
8232+
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
8233+
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
8234+
82258235
psl@^1.1.28:
82268236
version "1.15.0"
82278237
resolved "https://registry.yarnpkg.com/psl/-/psl-1.15.0.tgz#bdace31896f1d97cec6a79e8224898ce93d974c6"

0 commit comments

Comments
 (0)