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

refactor(mailclient): pass api key explicitly #419

Merged
merged 4 commits into from
Apr 6, 2022
Merged
Show file tree
Hide file tree
Changes from 3 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
11 changes: 3 additions & 8 deletions services/identity/MailClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,13 @@ import axios from "axios"

import logger from "@logger/logger"

const { POSTMAN_API_KEY } = process.env
const POSTMAN_API_URL = "https://api.postman.gov.sg/v1"

class MailClient {
POSTMAN_API_KEY: string

constructor() {
if (!POSTMAN_API_KEY) {
throw new Error("Postman.gov.sg API key cannot be empty.")
}

this.POSTMAN_API_KEY = POSTMAN_API_KEY
constructor(apiKey: string) {
this.POSTMAN_API_KEY = apiKey
}

async sendMail(recipient: string, body: string): Promise<void> {
Expand All @@ -29,7 +24,7 @@ class MailClient {
try {
await axios.post(endpoint, email, {
headers: {
Authorization: `Bearer ${POSTMAN_API_KEY}`,
Authorization: `Bearer ${this.POSTMAN_API_KEY}`,
},
})
} catch (err) {
Expand Down
37 changes: 1 addition & 36 deletions services/identity/__tests__/MailClient.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import _MailClient from "../MailClient"

const mockEndpoint = "https://api.postman.gov.sg/v1/transactional/email/send"

const MailClient = new _MailClient()
const MailClient = new _MailClient(process.env.POSTMAN_API_KEY!)

const generateEmail = (recipient: string, body: string) => ({
subject: "One-Time Password (OTP) for IsomerCMS",
Expand All @@ -21,20 +21,6 @@ const generateEmail = (recipient: string, body: string) => ({
})

describe("Mail Client", () => {
const OLD_ENV = process.env

beforeEach(() => {
// Clears the cache so imports in tests uses a fresh copy
jest.resetModules()
// Make a copy of existing environment
process.env = { ...OLD_ENV }
})

afterAll(() => {
// Restore old environment
process.env = OLD_ENV
})

afterEach(() => mockAxios.reset())

it("should return the result successfully when all parameters are valid", async () => {
Expand All @@ -53,27 +39,6 @@ describe("Mail Client", () => {
)
})

it("should throw an error on initialization when the env var is not set", async () => {
// Arrange
// Store the API key and set it later so that other tests are not affected
const curApiKey = process.env.POSTMAN_API_KEY
process.env.POSTMAN_API_KEY = ""
// NOTE: This is because of typescript transpiling down to raw js
// Export default compiles down to module.exports.default, which is also
// done by babel.
// Read more here: https://www.typescriptlang.org/tsconfig#allowSyntheticDefaultImports
const _MailClientWithoutKey = (await import("../MailClient")).default

// Act
// NOTE: We require a new instance because the old one would already have the API key bound
const actual = () => new _MailClientWithoutKey()

// Assert
expect(actual).toThrowError("Postman.gov.sg API key cannot be empty")
process.env.POSTMAN_API_KEY = curApiKey
expect(process.env.POSTMAN_API_KEY).toBe(curApiKey)
})

it("should return an error when a network error occurs", async () => {
// Arrange
const generatedEmail = generateEmail(mockRecipient, mockBody)
Expand Down
23 changes: 18 additions & 5 deletions services/identity/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,24 @@ import TokenStore from "./TokenStore"
import TotpGenerator from "./TotpGenerator"
import UsersService from "./UsersService"

const IS_LOCAL_DEV = process.env.NODE_ENV === "LOCAL_DEV"
const { OTP_EXPIRY, OTP_SECRET } = process.env
const {
OTP_EXPIRY,
OTP_SECRET,
NODE_ENV,
LOCAL_SITE_ACCESS_TOKEN,
POSTMAN_API_KEY,
} = process.env

const IS_LOCAL_DEV = NODE_ENV === "LOCAL_DEV"

const tokenStore = IS_LOCAL_DEV
? (({
getToken: (_apiTokenName: string) => process.env.LOCAL_SITE_ACCESS_TOKEN,
getToken: (_apiTokenName: string) => LOCAL_SITE_ACCESS_TOKEN,
} as unknown) as TokenStore)
: new TokenStore()

if (!OTP_SECRET) {
logger.error(
throw new Error(
"Please ensure that you have set OTP_SECRET in your env vars and that you have sourced them!"
)
}
Expand All @@ -33,10 +40,16 @@ const totpGenerator = new TotpGenerator({
expiry: parseInt(OTP_EXPIRY!, 10) ?? undefined,
})

if (!POSTMAN_API_KEY) {
throw new Error(
"Please ensure that you have set POSTMAN_API_KEY in your env vars and that you have sourced them!"
)
}

seaerchin marked this conversation as resolved.
Show resolved Hide resolved
const mockMailer = {
sendMail: (_email: string, html: string) => logger.info(html),
} as MailClient
const mailer = IS_LOCAL_DEV ? mockMailer : new MailClient()
const mailer = IS_LOCAL_DEV ? mockMailer : new MailClient(POSTMAN_API_KEY)

const smsClient = IS_LOCAL_DEV
? ({
Expand Down