-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement ses and submission endpoint
Merge pull request #3 from UKForeignOffice/implement-ses
- Loading branch information
Showing
67 changed files
with
6,781 additions
and
345 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,8 @@ | |
.yarn/build-state.yml | ||
.yarn/install-state.gz | ||
*.jest.* | ||
*.test.* | ||
jest | ||
README.md | ||
/api/src/__mocks__/ | ||
docs |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,4 +3,5 @@ | |
.yarn/cache | ||
.yarn/install-state.gz | ||
**/dist | ||
**/.env | ||
**/.env | ||
/api/coverage/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
{ | ||
"trailingComma": "es5", | ||
"singleQuote": false | ||
"singleQuote": false, | ||
"printWidth": 160 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
process.env.CNI_TEMPLATE_ID = "5678"; | ||
process.env.NODE_ENV = "test"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
module.exports = { | ||
presets: [ | ||
[ | ||
"@babel/preset-env", | ||
{ | ||
targets: { | ||
node: "18", | ||
}, | ||
}, | ||
], | ||
"@babel/preset-typescript", | ||
], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,9 @@ | ||
{ | ||
"port": "PORT", | ||
"env": "NODE_ENV" | ||
"env": "NODE_ENV", | ||
"documentPassword": "DOCUMENT_PASSWORD", | ||
"affirmationTemplate": "AFFIRMATION_TEMPLATE_ID", | ||
"cniTemplate": "CNI_TEMPLATE_ID", | ||
"submissionEmail": "SUBMISSION_EMAIL_ADDRESS", | ||
"senderEmail": "SENDER_EMAIL_ADDRESS" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,4 +7,7 @@ if (process.env.NODE_ENV !== "test") { | |
module.exports = { | ||
port: "9000", | ||
env: "development", | ||
documentPassword: "Sup3rS3cr3tP4ssw0rd", | ||
submissionAddress: "[email protected]", | ||
senderEmail: "[email protected]", | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"env": "test" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"testMatch": ["**/__tests__/**/*.test.ts"], | ||
"setupFiles": ["./.jest/setEnvVars.ts"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { ErrorCode, ERRORS, ErrorTypes } from "./errors"; | ||
|
||
type ApplicationErrorOptions = { | ||
isOperational: boolean; | ||
exposeToClient: boolean; | ||
}; | ||
|
||
const defaultOptions: ApplicationErrorOptions = { | ||
isOperational: true, | ||
exposeToClient: true, | ||
}; | ||
|
||
export class ApplicationError extends Error { | ||
/** | ||
* HTTP status code to send the response as. You may use {@link `Axios#HttpStatusCode`}, or the raw value | ||
*/ | ||
httpStatusCode: number; | ||
|
||
/** | ||
* Notarial-API error code to help identify the error and resolve it | ||
*/ | ||
code: ErrorCode; | ||
|
||
isOperational: boolean = true; | ||
|
||
/** | ||
* determines whether the error message will be included in the response to the client | ||
*/ | ||
exposeToClient: boolean = true; | ||
|
||
constructor(name: ErrorTypes, code: ErrorCode, httpStatusCode: number, message?: string, options?: Partial<ApplicationErrorOptions>) { | ||
super(message); | ||
this.name = name; | ||
this.httpStatusCode = httpStatusCode; | ||
this.code = code; | ||
this.message = message ?? ERRORS[name][code]; | ||
const { isOperational, exposeToClient } = { ...defaultOptions, ...options }; | ||
this.isOperational = isOperational; | ||
this.exposeToClient = exposeToClient; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { SESClient } from "@aws-sdk/client-ses"; | ||
|
||
export const ses = new SESClient({ | ||
region: "eu-west-2", | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { ses as sesClient } from "../SESClient"; | ||
import { MockProxy } from "jest-mock-extended"; | ||
import { SESClient } from "@aws-sdk/client-ses"; | ||
|
||
jest.mock("../SESClient", () => ({ | ||
__esModule: true, | ||
ses: { | ||
send: jest.fn(), | ||
}, | ||
})); | ||
|
||
export const ses = sesClient as unknown as MockProxy<SESClient>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
/** | ||
* These errors are for use in {@link ApplicationError}. | ||
* {@link ERRORS} can be imported and used anywhere. | ||
*/ | ||
|
||
/** | ||
* To add a new category of error (new {@link ErrorTypes}) | ||
* 1. Add a type to {@link ErrorTypes} | ||
* 2. Create a new type, named <YourNewType>ErrorCode | ||
* 3. Add error codes as a union, in snake case and capitalised, e.g. "SOME_ERROR_CODE" | ||
* 4. Add <YourNewType>ErrorCode to the {@link ErrorCodes union} | ||
* 5. Create a new const, named <YOUR_NEW_TYPE>, with the type {@link ErrorRecord}, e.g. `ErrorRecord<YourNewTypeErrorCode>` | ||
* 6. Add a new property and value to `ErrorRecords`, the key will be the ErrorType, and the value will be the const created in (5) | ||
* 7. Do the same for the const {@link ERRORS} | ||
*/ | ||
|
||
/** | ||
* Category of the error - this is likely to match the service it came from | ||
*/ | ||
export type ErrorTypes = "WEBHOOK" | "FILE" | "SES" | "GENERIC"; | ||
|
||
/** | ||
* Error code for the matching ErrorType. | ||
*/ | ||
type WebhookErrorCode = "EMPTY_PAYLOAD" | "EMPTY_TEMPLATE_DATA"; | ||
type FileErrorCode = "EMPTY_RES" | "API_ERROR" | "NOT_FOUND"; | ||
type SESErrorCode = | ||
| "NO_TEMPLATE" | ||
| "TEMPLATE_NOT_FOUND" | ||
| "TEMPLATE_PART_MISSING" | ||
| "TEMPLATE_VAR_MISSING" | ||
| "EMPTY_RES" | ||
| "BAD_REQUEST" | ||
| "API_ERROR" | ||
| "UNKNOWN"; | ||
|
||
type GenericErrorCode = "UNKNOWN" | "RATE_LIMIT_EXCEEDED"; | ||
|
||
/** | ||
* Union of all the different ErrorCode. | ||
*/ | ||
export type ErrorCode = WebhookErrorCode | FileErrorCode | SESErrorCode | GenericErrorCode; | ||
|
||
/** | ||
* {@ErrorRecord} uses `Record`, which means every key passed into the generic, must be implemented | ||
* for example, if there is a new ErrorCode for WebhookErrorCode, then the const WEBHOOK needs to implement | ||
* the new error code as a property. | ||
*/ | ||
type ErrorRecord<T extends ErrorCode> = Record<T, string>; | ||
|
||
const WEBHOOK: ErrorRecord<WebhookErrorCode> = { | ||
EMPTY_PAYLOAD: "Malformed form data: No questions property found", | ||
EMPTY_TEMPLATE_DATA: "No template data was returned", | ||
}; | ||
|
||
const FILE: ErrorRecord<FileErrorCode> = { | ||
EMPTY_RES: "The file server did not return a response", | ||
API_ERROR: "There was an error returning this file", | ||
NOT_FOUND: "The requested file could not be found", | ||
}; | ||
|
||
const SES: ErrorRecord<SESErrorCode> = { | ||
NO_TEMPLATE: "no template id was set for the specified form", | ||
TEMPLATE_NOT_FOUND: "no template with the specified id could be found", | ||
TEMPLATE_PART_MISSING: "the template subject line or body were missing", | ||
TEMPLATE_VAR_MISSING: "a required variable was missing from the template data", | ||
EMPTY_RES: "The email service did not return a response", | ||
BAD_REQUEST: "The email data being sent was malformed", | ||
API_ERROR: "The email service returned an error", | ||
UNKNOWN: "There was an unknown error sending the email", | ||
}; | ||
|
||
const GENERIC: ErrorRecord<GenericErrorCode> = { | ||
UNKNOWN: "Unknown error", | ||
RATE_LIMIT_EXCEEDED: "Rate limit exceeded", | ||
}; | ||
|
||
type ErrorRecords = { | ||
WEBHOOK: typeof WEBHOOK; | ||
FILE: typeof FILE; | ||
SES: typeof SES; | ||
GENERIC: typeof GENERIC; | ||
}; | ||
export const ERRORS: ErrorRecords = { | ||
WEBHOOK, | ||
FILE, | ||
SES, | ||
GENERIC, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { FormQuestion } from "../../../types/FormQuestion"; | ||
|
||
export function flattenQuestions(questions: FormQuestion[]) { | ||
return questions.flatMap(({ fields }) => { | ||
return fields; | ||
}); | ||
} |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { post } from "./post"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { NextFunction, Request, Response } from "express"; | ||
|
||
export async function post(req: Request, res: Response, next: NextFunction) { | ||
const { submitService } = res.app.services; | ||
try { | ||
const { reference } = await submitService.submitForm(req.body); | ||
res.status(200).send({ | ||
message: "Email sent successfully", | ||
reference, | ||
}); | ||
} catch (e) { | ||
next(e); | ||
return; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { getAllInputsFromForm } from "./getAllInputsFromForm"; | ||
import { FormDataBody } from "../../types"; | ||
import { getTemplateDataFromInputs } from "./getTemplateDataFromInputs"; | ||
|
||
export type Errors = { | ||
errors: Error; | ||
}; | ||
|
||
export function buildEmailData( | ||
formBody: FormDataBody, | ||
formType: "cni" | "affirmation" | ||
) { | ||
const fields = getAllInputsFromForm(formBody); | ||
if (!fields) { | ||
return { | ||
errors: new Error("Malformed form data: No questions property found"), | ||
} as Errors; | ||
} | ||
const templateData = getTemplateDataFromInputs(fields, formType); | ||
return templateData; | ||
} |
Oops, something went wrong.